ORM Party

Admin

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/express

Register 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",
          },
        },
      },
    },
  ],
});