Select ORMs to compare
Pick one or more ORMs from the bar above
Install
SQL
Not applicable for this ORM
Django ORM
# Built-in — no extra installation needed
# django.contrib.admin is included by default
SQLAlchemy
pip install sqladmin
SQLModel
pip install sqladmin
Tortoise ORM
pip install fastadmin
Peewee
pip install flask-admin flask-login
Prisma
# Built-in — Prisma Studio is included with prisma
npx prisma studio
Drizzle
# Built-in — Drizzle Studio is included with drizzle-kit
npx drizzle-kit studio Kysely
Not applicable for this ORM
TypeORM
npm install adminjs @adminjs/typeorm @adminjs/express
MikroORM
npm install adminjs @adminjs/mikroorm @adminjs/express
Sequelize
npm install adminjs @adminjs/sequelize @adminjs/expressRegister Model
SQL
Not applicable for this ORM
Django ORM
# Shows __str__ representation by default
from django.contrib import admin
from .models import User
@admin.register(User)
class UserAdmin(admin.ModelAdmin):
pass
SQLAlchemy
# All fields displayed by default
from sqladmin import Admin, ModelView
admin = Admin(app, engine)
class UserAdmin(ModelView, model=User):
pass
admin.add_view(UserAdmin)
SQLModel
# All fields displayed by default
from sqladmin import Admin, ModelView
admin = Admin(app, engine)
class UserAdmin(ModelView, model=User):
pass
admin.add_view(UserAdmin)
Tortoise ORM
# Shows __str__ representation by default
from fastadmin import TortoiseModelAdmin, register
@register(User)
class UserAdmin(TortoiseModelAdmin):
pass
Peewee
# All fields displayed by default
from flask_admin import Admin
from flask_admin.contrib.peewee import ModelView
admin = Admin(app)
admin.add_view(ModelView(User))
Prisma
# Prisma Studio — built-in visual editor
npx prisma studio
# Opens browser at http://localhost:5555
# All models from schema.prisma are available automatically
Drizzle
# Drizzle Studio — built-in visual editor
npx drizzle-kit studio
# Opens browser at https://local.drizzle.studio
# All tables from your schema are available automatically Kysely
Not applicable for this ORM
TypeORM
// All fields displayed by default
import AdminJS from "adminjs";
import * as AdminJSTypeORM from "@adminjs/typeorm";
import { User } from "./entity";
AdminJS.registerAdapter(AdminJSTypeORM);
const admin = new AdminJS({
resources: [User],
});
MikroORM
// All fields displayed by default
import AdminJS from "adminjs";
import * as AdminJSMikroORM from "@adminjs/mikroorm";
AdminJS.registerAdapter(AdminJSMikroORM);
const admin = new AdminJS({
resources: [{ resource: { model: User, orm } }],
});
Sequelize
// All fields displayed by default
import AdminJS from "adminjs";
import * as AdminJSSequelize from "@adminjs/sequelize";
import { User } from "./models";
AdminJS.registerAdapter(AdminJSSequelize);
const admin = new AdminJS({
resources: [User],
});List Display
SQL
Not applicable for this ORM
Django ORM
from django.contrib import admin
from .models import User
@admin.register(User)
class UserAdmin(admin.ModelAdmin):
list_display = ["name", "email", "created_at"]
ordering = ["-created_at"]
SQLAlchemy
from sqladmin import Admin, ModelView
admin = Admin(app, engine)
class UserAdmin(ModelView, model=User):
column_list = [User.name, User.email, User.created_at]
column_default_sort = ("created_at", True)
admin.add_view(UserAdmin)
SQLModel
from sqladmin import Admin, ModelView
admin = Admin(app, engine)
class UserAdmin(ModelView, model=User):
column_list = [User.name, User.email, User.created_at]
column_default_sort = ("created_at", True)
admin.add_view(UserAdmin)
Tortoise ORM
from fastadmin import TortoiseModelAdmin, register
@register(User)
class UserAdmin(TortoiseModelAdmin):
list_display = ["name", "email", "created_at"]
ordering = ["-created_at"]
Peewee
from flask_admin import Admin
from flask_admin.contrib.peewee import ModelView
admin = Admin(app)
class UserAdmin(ModelView):
column_list = ["name", "email", "created_at"]
column_default_sort = ("created_at", True)
admin.add_view(UserAdmin(User))
Prisma
# All fields displayed automatically — no configuration needed
# Click column headers to sort (ascending/descending)
npx prisma studio
Drizzle
# All fields displayed automatically — no configuration needed
# Click column headers to sort (ascending/descending)
npx drizzle-kit studio Kysely
Not applicable for this ORM
TypeORM
import AdminJS from "adminjs";
import * as AdminJSTypeORM from "@adminjs/typeorm";
import { User } from "./entity";
AdminJS.registerAdapter(AdminJSTypeORM);
const admin = new AdminJS({
resources: [
{
resource: User,
options: {
listProperties: ["name", "email", "createdAt"],
sort: { sortBy: "createdAt", direction: "desc" },
},
},
],
});
MikroORM
import AdminJS from "adminjs";
import * as AdminJSMikroORM from "@adminjs/mikroorm";
import { User } from "./entity";
AdminJS.registerAdapter(AdminJSMikroORM);
const admin = new AdminJS({
resources: [
{
resource: { model: User, orm },
options: {
listProperties: ["name", "email", "createdAt"],
sort: { sortBy: "createdAt", direction: "desc" },
},
},
],
});
Sequelize
import AdminJS from "adminjs";
import * as AdminJSSequelize from "@adminjs/sequelize";
import { User } from "./models";
AdminJS.registerAdapter(AdminJSSequelize);
const admin = new AdminJS({
resources: [
{
resource: User,
options: {
listProperties: ["name", "email", "createdAt"],
sort: { sortBy: "createdAt", direction: "desc" },
},
},
],
});Search & Filters
SQL
Not applicable for this ORM
Django ORM
from django.contrib import admin
from .models import User
@admin.register(User)
class UserAdmin(admin.ModelAdmin):
list_display = ["name", "email", "created_at"]
search_fields = ["name", "email"]
list_filter = ["created_at"]
SQLAlchemy
from sqladmin import Admin, ModelView
admin = Admin(app, engine)
class UserAdmin(ModelView, model=User):
column_list = [User.name, User.email, User.created_at]
column_searchable_list = [User.name, User.email]
column_filters = [User.created_at]
admin.add_view(UserAdmin)
SQLModel
from sqladmin import Admin, ModelView
admin = Admin(app, engine)
class UserAdmin(ModelView, model=User):
column_list = [User.name, User.email, User.created_at]
column_searchable_list = [User.name, User.email]
column_filters = [User.created_at]
admin.add_view(UserAdmin)
Tortoise ORM
from fastadmin import TortoiseModelAdmin, register
@register(User)
class UserAdmin(TortoiseModelAdmin):
list_display = ["name", "email", "created_at"]
search_fields = ["name", "email"]
list_filter = ["created_at"]
Peewee
from flask_admin import Admin
from flask_admin.contrib.peewee import ModelView
admin = Admin(app)
class UserAdmin(ModelView):
column_list = ["name", "email", "created_at"]
column_searchable_list = ["name", "email"]
column_filters = ["created_at"]
admin.add_view(UserAdmin(User))
Prisma
# Built-in search and filter — no configuration needed
# Prisma Studio provides filtering on all fields automatically
npx prisma studio
Drizzle
# Built-in search and filter — no configuration needed
# Drizzle Studio provides filtering on all fields automatically
npx drizzle-kit studio Kysely
Not applicable for this ORM
TypeORM
import AdminJS from "adminjs";
import * as AdminJSTypeORM from "@adminjs/typeorm";
import { User } from "./entity";
AdminJS.registerAdapter(AdminJSTypeORM);
const admin = new AdminJS({
resources: [
{
resource: User,
options: {
listProperties: ["name", "email", "createdAt"],
properties: {
name: { isTitle: true },
},
filterProperties: ["name", "email", "createdAt"],
},
},
],
});
MikroORM
import AdminJS from "adminjs";
import * as AdminJSMikroORM from "@adminjs/mikroorm";
import { User } from "./entity";
AdminJS.registerAdapter(AdminJSMikroORM);
const admin = new AdminJS({
resources: [
{
resource: { model: User, orm },
options: {
listProperties: ["name", "email", "createdAt"],
properties: {
name: { isTitle: true },
},
filterProperties: ["name", "email", "createdAt"],
},
},
],
});
Sequelize
import AdminJS from "adminjs";
import * as AdminJSSequelize from "@adminjs/sequelize";
import { User } from "./models";
AdminJS.registerAdapter(AdminJSSequelize);
const admin = new AdminJS({
resources: [
{
resource: User,
options: {
listProperties: ["name", "email", "createdAt"],
properties: {
name: { isTitle: true },
},
filterProperties: ["name", "email", "createdAt"],
},
},
],
});Custom Actions
SQL
Not applicable for this ORM
Django ORM
from django.contrib import admin
from .models import User
@admin.register(User)
class UserAdmin(admin.ModelAdmin):
actions = ["deactivate_users"]
@admin.action(description="Deactivate selected users")
def deactivate_users(self, request, queryset):
queryset.update(is_active=False)
SQLAlchemy
from sqladmin import Admin, ModelView, action
from sqlalchemy.orm import Session
admin = Admin(app, engine)
class UserAdmin(ModelView, model=User):
@action(name="deactivate", label="Deactivate users")
def deactivate_action(self, request, pks):
with Session(engine) as session:
for pk in pks:
user = session.get(User, int(pk))
if user:
user.is_active = False
session.commit()
return "{}"
admin.add_view(UserAdmin)
SQLModel
from sqladmin import Admin, ModelView, action
from sqlmodel import Session
admin = Admin(app, engine)
class UserAdmin(ModelView, model=User):
@action(name="deactivate", label="Deactivate users")
def deactivate_action(self, request, pks):
with Session(engine) as session:
for pk in pks:
user = session.get(User, int(pk))
if user:
user.is_active = False
session.commit()
return "{}"
admin.add_view(UserAdmin)
Tortoise ORM
# FastAdmin doesn't support custom bulk actions
# Handle actions in your application code instead
Peewee
from flask_admin import Admin
from flask_admin.contrib.peewee import ModelView
from flask_admin.actions import action
admin = Admin(app)
class UserAdmin(ModelView):
@action("deactivate", "Deactivate", "Are you sure?")
def action_deactivate(self, ids):
User.update(is_active=False).where(User.id.in_(ids)).execute()
admin.add_view(UserAdmin(User))
Prisma
# Prisma Studio doesn't support custom actions
# Handle actions in your application code instead
npx prisma studio
Drizzle
# Drizzle Studio doesn't support custom actions
# Handle actions in your application code instead
npx drizzle-kit studio Kysely
Not applicable for this ORM
TypeORM
import AdminJS from "adminjs";
import * as AdminJSTypeORM from "@adminjs/typeorm";
import { User } from "./entity";
AdminJS.registerAdapter(AdminJSTypeORM);
const admin = new AdminJS({
resources: [
{
resource: User,
options: {
actions: {
deactivate: {
actionType: "bulk",
component: false,
handler: async (request, response, context) => {
const { records } = context;
await Promise.all(
records.map((r) => r.update({ isActive: false }))
);
return {
records: records.map((r) => r.toJSON()),
};
},
},
},
},
},
],
});
MikroORM
import AdminJS from "adminjs";
import * as AdminJSMikroORM from "@adminjs/mikroorm";
import { User } from "./entity";
AdminJS.registerAdapter(AdminJSMikroORM);
const admin = new AdminJS({
resources: [
{
resource: { model: User, orm },
options: {
actions: {
deactivate: {
actionType: "bulk",
component: false,
handler: async (request, response, context) => {
const { records } = context;
await Promise.all(
records.map((r) => r.update({ isActive: false }))
);
return {
records: records.map((r) => r.toJSON()),
};
},
},
},
},
},
],
});
Sequelize
import AdminJS from "adminjs";
import * as AdminJSSequelize from "@adminjs/sequelize";
import { User } from "./models";
AdminJS.registerAdapter(AdminJSSequelize);
const admin = new AdminJS({
resources: [
{
resource: User,
options: {
actions: {
deactivate: {
actionType: "bulk",
component: false,
handler: async (request, response, context) => {
const { records } = context;
await Promise.all(
records.map((r) => r.update({ isActive: false }))
);
return {
records: records.map((r) => r.toJSON()),
};
},
},
},
},
},
],
});Inline Editing
SQL
Not applicable for this ORM
Django ORM
from django.contrib import admin
from .models import User, Post
class PostInline(admin.TabularInline):
model = Post
extra = 1
@admin.register(User)
class UserAdmin(admin.ModelAdmin):
inlines = [PostInline]
SQLAlchemy
# sqladmin doesn't support inline editing
# Edit related models through their own admin view
from sqladmin import Admin, ModelView
admin = Admin(app, engine)
class UserAdmin(ModelView, model=User):
column_details_list = [User.name, User.email, User.posts]
admin.add_view(UserAdmin)
SQLModel
# sqladmin doesn't support inline editing
# Edit related models through their own admin view
from sqladmin import Admin, ModelView
admin = Admin(app, engine)
class UserAdmin(ModelView, model=User):
column_details_list = [User.name, User.email, User.posts]
admin.add_view(UserAdmin)
Tortoise ORM
# FastAdmin doesn't support inline editing
# Edit related models through their own admin view
Peewee
from flask_admin import Admin
from flask_admin.contrib.peewee import ModelView
from flask_admin.contrib.peewee.form import InlineFormAdmin
admin = Admin(app)
class PostInline(InlineFormAdmin):
form_columns = ["id", "title", "body"]
class UserAdmin(ModelView):
inline_models = [PostInline(Post)]
admin.add_view(UserAdmin(User))
Prisma
# Prisma Studio doesn't support inline editing
# Edit related models through their own table view
npx prisma studio
Drizzle
# Drizzle Studio doesn't support inline editing
# Edit related models through their own table view
npx drizzle-kit studio Kysely
Not applicable for this ORM
TypeORM
// AdminJS doesn't support inline editing
// Edit related models through their own resource page
import AdminJS from "adminjs";
import * as AdminJSTypeORM from "@adminjs/typeorm";
import { User } from "./entity";
AdminJS.registerAdapter(AdminJSTypeORM);
const admin = new AdminJS({
resources: [
{
resource: User,
options: {
properties: {
posts: { isVisible: { list: false, show: true } },
},
},
},
],
});
MikroORM
// AdminJS doesn't support inline editing
// Edit related models through their own resource page
import AdminJS from "adminjs";
import * as AdminJSMikroORM from "@adminjs/mikroorm";
import { User } from "./entity";
AdminJS.registerAdapter(AdminJSMikroORM);
const admin = new AdminJS({
resources: [
{
resource: { model: User, orm },
options: {
properties: {
posts: { isVisible: { list: false, show: true } },
},
},
},
],
});
Sequelize
// AdminJS doesn't support inline editing
// Edit related models through their own resource page
import AdminJS from "adminjs";
import * as AdminJSSequelize from "@adminjs/sequelize";
import { User } from "./models";
AdminJS.registerAdapter(AdminJSSequelize);
const admin = new AdminJS({
resources: [
{
resource: User,
options: {
properties: {
posts: { isVisible: { list: false, show: true } },
},
},
},
],
});Fieldsets & Readonly
SQL
Not applicable for this ORM
Django ORM
from django.contrib import admin
from .models import User
@admin.register(User)
class UserAdmin(admin.ModelAdmin):
fieldsets = [
(None, {"fields": ["name", "email"]}),
("Status", {"fields": ["is_active", "role"]}),
("Timestamps", {
"classes": ["collapse"],
"fields": ["created_at", "updated_at"],
}),
]
readonly_fields = ["created_at", "updated_at"]
SQLAlchemy
from sqladmin import Admin, ModelView
admin = Admin(app, engine)
class UserAdmin(ModelView, model=User):
form_columns = [User.name, User.email, User.is_active, User.role]
form_excluded_columns = [User.created_at, User.updated_at]
column_details_list = [
User.name, User.email, User.is_active,
User.role, User.created_at, User.updated_at,
]
admin.add_view(UserAdmin)
SQLModel
from sqladmin import Admin, ModelView
admin = Admin(app, engine)
class UserAdmin(ModelView, model=User):
form_columns = [User.name, User.email, User.is_active, User.role]
form_excluded_columns = [User.created_at, User.updated_at]
column_details_list = [
User.name, User.email, User.is_active,
User.role, User.created_at, User.updated_at,
]
admin.add_view(UserAdmin)
Tortoise ORM
from fastadmin import TortoiseModelAdmin, register
@register(User)
class UserAdmin(TortoiseModelAdmin):
fieldsets = [
(None, {"fields": ["name", "email"]}),
("Status", {"fields": ["is_active", "role"]}),
]
readonly_fields = ["created_at", "updated_at"]
Peewee
from flask_admin import Admin
from flask_admin.contrib.peewee import ModelView
admin = Admin(app)
class UserAdmin(ModelView):
form_columns = ["name", "email", "is_active", "role", "created_at", "updated_at"]
form_widget_args = {
"created_at": {"readonly": True},
"updated_at": {"readonly": True},
}
admin.add_view(UserAdmin(User))
Prisma
# Prisma Studio doesn't support fieldsets or readonly fields
# All fields are editable in the built-in editor
npx prisma studio
Drizzle
# Drizzle Studio doesn't support fieldsets or readonly fields
# All fields are editable in the built-in editor
npx drizzle-kit studio Kysely
Not applicable for this ORM
TypeORM
import AdminJS from "adminjs";
import * as AdminJSTypeORM from "@adminjs/typeorm";
import { User } from "./entity";
AdminJS.registerAdapter(AdminJSTypeORM);
const admin = new AdminJS({
resources: [
{
resource: User,
options: {
properties: {
createdAt: {
isVisible: { edit: false, show: true, list: true },
},
updatedAt: {
isVisible: { edit: false, show: true, list: true },
},
},
},
},
],
});
MikroORM
import AdminJS from "adminjs";
import * as AdminJSMikroORM from "@adminjs/mikroorm";
import { User } from "./entity";
AdminJS.registerAdapter(AdminJSMikroORM);
const admin = new AdminJS({
resources: [
{
resource: { model: User, orm },
options: {
properties: {
createdAt: {
isVisible: { edit: false, show: true, list: true },
},
updatedAt: {
isVisible: { edit: false, show: true, list: true },
},
},
},
},
],
});
Sequelize
import AdminJS from "adminjs";
import * as AdminJSSequelize from "@adminjs/sequelize";
import { User } from "./models";
AdminJS.registerAdapter(AdminJSSequelize);
const admin = new AdminJS({
resources: [
{
resource: User,
options: {
properties: {
createdAt: {
isVisible: { edit: false, show: true, list: true },
},
updatedAt: {
isVisible: { edit: false, show: true, list: true },
},
},
},
},
],
});Permissions
SQL
Not applicable for this ORM
Django ORM
from django.contrib import admin
from .models import User
@admin.register(User)
class UserAdmin(admin.ModelAdmin):
def has_delete_permission(self, request, obj=None):
return request.user.is_superuser
def has_change_permission(self, request, obj=None):
return request.user.has_perm("app.change_user")
def get_readonly_fields(self, request, obj=None):
if not request.user.is_superuser:
return ["email", "role"]
return []
SQLAlchemy
from sqladmin import Admin, ModelView
from sqladmin.authentication import AuthenticationBackend
from starlette.requests import Request
class AdminAuth(AuthenticationBackend):
async def login(self, request: Request) -> bool:
form = await request.form()
# validate credentials
request.session.update({"token": "authenticated"})
return True
async def logout(self, request: Request) -> bool:
request.session.clear()
return True
async def authenticate(self, request: Request) -> bool:
return "token" in request.session
admin = Admin(app, engine, authentication_backend=AdminAuth(secret_key="..."))
SQLModel
from sqladmin import Admin, ModelView
from sqladmin.authentication import AuthenticationBackend
from starlette.requests import Request
class AdminAuth(AuthenticationBackend):
async def login(self, request: Request) -> bool:
form = await request.form()
# validate credentials
request.session.update({"token": "authenticated"})
return True
async def logout(self, request: Request) -> bool:
request.session.clear()
return True
async def authenticate(self, request: Request) -> bool:
return "token" in request.session
admin = Admin(app, engine, authentication_backend=AdminAuth(secret_key="..."))
Tortoise ORM
from fastadmin import TortoiseModelAdmin, register
@register(User)
class UserAdmin(TortoiseModelAdmin):
async def has_delete_permission(self, user_id: int) -> bool:
user = await User.get(id=user_id)
return user.is_superuser
async def has_change_permission(self, user_id: int) -> bool:
user = await User.get(id=user_id)
return user.is_active
Peewee
from flask import redirect, url_for
from flask_admin import Admin
from flask_admin.contrib.peewee import ModelView
from flask_login import current_user
admin = Admin(app)
class AuthModelView(ModelView):
def is_accessible(self):
return current_user.is_authenticated and current_user.is_admin
def inaccessible_callback(self, name, **kwargs):
return redirect(url_for("login"))
admin.add_view(AuthModelView(User))
Prisma
# Prisma Studio doesn't support authentication or permissions
# Access control must be handled at the network level
npx prisma studio
Drizzle
# Drizzle Studio doesn't support authentication or permissions
# Access control must be handled at the network level
npx drizzle-kit studio Kysely
Not applicable for this ORM
TypeORM
import AdminJS from "adminjs";
import * as AdminJSTypeORM from "@adminjs/typeorm";
import { User } from "./entity";
AdminJS.registerAdapter(AdminJSTypeORM);
const admin = new AdminJS({
resources: [
{
resource: User,
options: {
actions: {
delete: {
isAccessible: ({ currentAdmin }) =>
currentAdmin?.role === "admin",
},
edit: {
isAccessible: ({ currentAdmin }) =>
currentAdmin?.role === "admin",
},
},
},
},
],
});
MikroORM
import AdminJS from "adminjs";
import * as AdminJSMikroORM from "@adminjs/mikroorm";
import { User } from "./entity";
AdminJS.registerAdapter(AdminJSMikroORM);
const admin = new AdminJS({
resources: [
{
resource: { model: User, orm },
options: {
actions: {
delete: {
isAccessible: ({ currentAdmin }) =>
currentAdmin?.role === "admin",
},
edit: {
isAccessible: ({ currentAdmin }) =>
currentAdmin?.role === "admin",
},
},
},
},
],
});
Sequelize
import AdminJS from "adminjs";
import * as AdminJSSequelize from "@adminjs/sequelize";
import { User } from "./models";
AdminJS.registerAdapter(AdminJSSequelize);
const admin = new AdminJS({
resources: [
{
resource: User,
options: {
actions: {
delete: {
isAccessible: ({ currentAdmin }) =>
currentAdmin?.role === "admin",
},
edit: {
isAccessible: ({ currentAdmin }) =>
currentAdmin?.role === "admin",
},
},
},
},
],
});