Skip to content
New documentation  ยท  See what changed

Extensible admin interfaces
for FastAPI & Starlette

Generate a complete admin interface from your SQLAlchemy, SQLModel, Beanie, MongoEngine, or Tortoise ORM models. Built on the Tabler UI kit, starlette-admin gives you list views, auto-generated forms, data exports, and secure authentication. Configure the whole interface in Python, without writing any frontend code.

pip install starlette-admin
starlette-admin dashboard showing statistical widgets, recent activity tables, and a sidebar for model views

Built-in features

Everything you need works out of the box. Every core feature includes documented extension points, so you can adapt it to your requirements.

Everything is Python

Build a complete admin interface with a pure Python API designed for rapid development, readable syntax, and long-term maintainability.

Mount the admin panel

Register a model and mount the admin panel on any FastAPI or Starlette application. Then run fastapi dev and open /admin.

View the documentation

main.py
from fastapi import FastAPI
from sqlalchemy import create_engine
from starlette_admin.contrib.sqla import Admin, ModelView

from models import Base, Post

engine = create_engine("sqlite:///blog.db")
Base.metadata.create_all(engine)

app = FastAPI()

admin = Admin(engine, title="Blog Admin", secret_key="change-me")
admin.add_view(ModelView(Post, icon="fa fa-newspaper"))
admin.mount_to(app)

Views

Set search, sorting, default ordering, and export formats with plain class attributes. Arrange your create and edit forms with form_layout.

View the documentation

views.py
from starlette_admin.contrib.sqla import ModelView


class PostView(ModelView):
    fields = ["id", "title", "author", "content", "published", "created_at"]
    searchable_fields = ["title", "content"]
    fields_default_sort = [("created_at", True)]
    exporters = ["csv", "xlsx", "json"]
    form_layout = [
        ("title", "author"),
        "content",
        ("published", "created_at"),
    ]


admin.add_view(PostView(Post, icon="fa fa-newspaper"))

Fields

Override any auto-detected field to control validation, per-page visibility, and how starlette-admin reads and displays values.

View the documentation

views.py
from starlette_admin import DateTimeField, RequestAction, StringField, TextAreaField
from starlette_admin.contrib.sqla import ModelView


class PostView(ModelView):
    fields = [
        "id",
        StringField("title", required=True, help_text="Shown on the blog"),
        TextAreaField("content", exclude_from_list=True),
        StringField(
            "author_email",
            getter=lambda request, obj: obj.author.email,
            formatter={RequestAction.LIST: lambda request, value: value or "unset"},
        ),
        DateTimeField("created_at", read_only=True, exclude_from_create=True),
    ]

Filters

Extend the built-in query builder with custom filters that match your business rules. You can apply the operations you need directly to the underlying database model.

View the documentation

filters.py
from datetime import datetime
from typing import Any

from starlette_admin.contrib.sqla import ModelView
from starlette_admin.filters.base import BaseFilter, FilterApplyContext, FilterDataType


class ActiveThisMonthFilter(BaseFilter):
    name = "this_month"
    label = "Created this month"
    data_type = FilterDataType.NONE  # No value input. The range comes from now().

    def apply(self, ctx: FilterApplyContext) -> Any:
        now = datetime.utcnow()
        start = now.replace(day=1, hour=0, minute=0, second=0, microsecond=0)
        col = getattr(ctx.view.model, ctx.field_name)
        return col.between(start, now)

class ProductView(ModelView):
    fields = [
        DateTimeField(
            "created_at",
            filters=[ActiveThisMonthFilter, ...],
        ),
    ]

Actions

Attach business operations with a single decorator. Confirmation modals, custom forms, and flash messages are built into the framework.

View the documentation

views.py
from starlette.requests import Request
from starlette_admin import ActionSelection, action, flash
from starlette_admin.contrib.sqla import ModelView


class ArticleView(ModelView):
    actions = ["publish", "delete"]

    @action(
        name="publish",
        text="Mark as published",
        confirmation="Publish the selected articles?",
        submit_btn_text="Yes, publish",
    )
    async def publish(self, request: Request, selection: ActionSelection) -> None:
        articles = await selection.rows()
        for article in articles:
            article.published = True
        flash(request, f"{len(articles)} articles published.", "success")

Authentication

Implement three standard methods around your own credential check. starlette-admin handles the login page, sessions, and redirects for you.

View the documentation

auth.py
from starlette.requests import Request
from starlette_admin.auth import AdminUser, AuthProvider, LoginFailed


class MyAuthProvider(AuthProvider):
    async def login(self, username, password, remember_me, request: Request) -> None:
        if not await check_credentials(username, password):
            raise LoginFailed("Invalid username or password")
        request.session["username"] = username

    async def authenticate(self, request: Request) -> AdminUser | None:
        if username := request.session.get("username"):
            return AdminUser(username=username)
        return None

    async def logout(self, request: Request) -> None:
        request.session.clear()


admin = Admin(engine, auth_provider=MyAuthProvider(), secret_key=SECRET)

Dashboard

Compose the admin home page from statistic, chart, and table widgets that query live data on every request.

View the documentation

dashboard.py
from starlette_admin import CardRowWidget, ChartWidget, CustomView, StatWidget

dashboard = CardRowWidget(
    children=[
        StatWidget(title="Orders", value_callback=count_orders, countup=True),
        StatWidget(title="Revenue", value_callback=sum_revenue, color="success"),
        ChartWidget(title="Sales", chart_type="area", series_callback=sales_series),
    ]
)

admin = Admin(
    engine,
    title="Shop Admin",
    secret_key="change-me",
    index_view=CustomView(menu_label="Dashboard", icon="fa fa-home", widget=dashboard),
)

Plugins & extensions

Every layer is replaceable. Package features as self-contained plugins, or hook into a dedicated extension point to tailor the framework to your domain.

Drop-in plugins

Zero-boilerplate plugins

Install a plugin package and pass it to your Admin instance. Fields, converters, templates, and assets wire themselves together automatically.

from starlette_admin_geospatial import GeospatialPlugin
from starlette_admin.contrib.sqla import Admin

admin = Admin(
    engine,
    plugins=[GeospatialPlugin(default_zoom=13)],
)
Read the plugins guide
Extension points

Hook into any component

Predefined interfaces let you swap or extend each concern independently. Subclass the base class you need and register it. You can customize everything from the authentication flow to the export formats.

Explore all extension points