Skip to content

Admin

unfazed.contrib.admin provides an automatic admin interface for managing your Tortoise ORM models. You register model admins with decorators, and Unfazed serves a JSON API that a frontend (e.g. Ant Design Pro) can consume to render CRUD pages, search panels, inline relations, and custom actions.

Quick Start

1. Install the app, lifespan, and routes

# entry/settings/__init__.py

UNFAZED_SETTINGS = {
    ...
    "INSTALLED_APPS": [
        "unfazed.contrib.auth",
        "unfazed.contrib.admin",
        "apps.blog",
    ],
    "LIFESPAN": [
        "unfazed.contrib.admin.registry.lifespan.AdminWakeup",
    ],
}
# entry/routes.py
from unfazed.route import path, include

patterns = [
    path("/api/contrib/admin", routes=include("unfazed.contrib.admin.routes")),
]

2. Configure admin settings

# entry/settings/__init__.py

UNFAZED_CONTRIB_ADMIN_SETTINGS = {
    "TITLE": "My Project Admin",
    "API_PREFIX": "/api/contrib/admin",
}

3. Register a model admin

Create an admin.py in your app:

# apps/blog/admin.py
from unfazed.contrib.admin.registry import ModelAdmin, register
from apps.blog.serializers import PostSerializer


@register(PostSerializer)
class PostAdmin(ModelAdmin):
    list_display = ["id", "title", "published", "created_at"]
    search_fields = ["title"]
    list_filter = ["published"]
    datetime_fields = ["created_at"]
    readonly_fields = ["id", "created_at"]

The AdminWakeup lifespan automatically discovers admin.py modules in all installed apps at startup.

Configuration

Admin settings are registered under UNFAZED_CONTRIB_ADMIN_SETTINGS:

Key Type Default Description
TITLE str "Unfazed Admin" Admin site title.
LOGO str Ant Design logo URL Logo URL.
API_PREFIX str "/api/contrib/admin" Prefix for all admin API endpoints.
WEBSITE_PREFIX str "/admin" Frontend URL prefix.
TIME_ZONE str "UTC" Display timezone.
SHOW_WATERMARK bool True Show watermark on the admin site.
DEFAULT_LOGIN_TYPE bool True Use default account/password login form.
AUTH_PLUGINS List[AuthPlugin] [] OAuth login buttons (each has ICON_URL and PLATFORM).
ICONFONT_URL str "" Custom iconfont URL.
EXTRA Dict {} Additional config passed to the frontend.

Admin API Endpoints

When you include unfazed.contrib.admin.routes, the following endpoints are registered:

Method Path Description
GET /route-list Return registered admin routes (menu tree).
GET /settings Return site settings (theme, title, logo, etc.).
POST /model-desc Return field definitions and attributes for a model admin.
POST /model-data Query and paginate model data.
POST /model-inlines Return inline relation data for a record.
POST /model-save Create or update a record.
POST /model-delete Delete a record.
POST /model-action Execute a custom action.

All endpoints except /settings require @login_required.

Admin Classes

ModelAdmin

The primary admin class for registering a Tortoise model:

@register(PostSerializer)
class PostAdmin(ModelAdmin):
    ...

List page options:

Attribute Type Default Description
list_display List[str] all fields Columns shown in the list view.
list_sort List[str] [] Columns with click-to-sort enabled.
list_search List[str] [] Columns with in-table search.
list_filter List[str] [] Fields shown as filter options.
list_editable List[str] [] Fields editable inline on the list page.
list_order List[str] [] Default ordering.
list_per_page int 20 Default page size.
list_per_page_options List[int] [10,20,50,100] Page size options.

Detail page options:

Attribute Type Default Description
detail_display List[str] all db fields Fields shown in the detail view.
detail_order List[str] [] Display order of fields.
detail_editable List[str] [] Editable fields in the detail view.

Field type hints:

Attribute Description
image_fields Render as image preview.
datetime_fields Render as date/time picker.
editor_fields Render as rich-text editor.
json_fields Render as JSON editor.
readonly_fields Fields that cannot be edited.
not_null_fields Fields marked as required.

Search panel:

Attribute Description
search_fields Fields shown in the search panel above the table.
search_range_fields Subset of search_fields rendered as range inputs.

Behaviour flags:

Attribute Default Description
can_add True Show an "Add" button.
can_delete True Show a "Delete" button.
can_edit True Allow editing records.
can_search True Enable the search panel.

Navigation:

Attribute Description
help_text Descriptive text for the admin page.
icon Menu icon.
hideInMenu Hide from the navigation menu.
hideChildrenInMenu Hide child routes.

ModelInlineAdmin

Used for related models displayed as inline tables within a parent ModelAdmin:

@register(CommentSerializer)
class CommentInlineAdmin(ModelInlineAdmin):
    list_display = ["id", "body", "created_at"]
    max_num = 50
    min_num = 0

Wire it to a parent via inlines:

from unfazed.contrib.admin.registry import AdminRelation

@register(PostSerializer)
class PostAdmin(ModelAdmin):
    inlines = [
        AdminRelation(target="CommentInlineAdmin"),
    ]

Unfazed auto-detects the relation type and join fields when possible (AutoFill). For M2M relations, you must provide a through configuration with the AdminThrough schema.

CustomAdmin

For admin pages that are not tied to a model — dashboards, reports, or utility pages:

from unfazed.contrib.admin.registry import CustomAdmin, register, action, ActionOutput
from unfazed.contrib.admin.registry.fields import Field


@register()
class DashboardAdmin(CustomAdmin):
    component = "ModelCustom"
    fields_set = [
        Field(name="date_range", field_type="DatetimeField"),
    ]

    @action(name="export_report", label="Export Report", output=ActionOutput.Download)
    async def export_report(self, ctx):
        ...

The @register Decorator

@register(serializer_cls=None)
class MyAdmin(ModelAdmin): ...

Registers an admin instance in the global admin_collector. Pass a Serializer subclass to automatically set the admin's serializer. If the admin class has override = True, it replaces any existing registration with the same name.

The @action Decorator

Define custom actions on admin classes:

from unfazed.contrib.admin.registry import action, ActionOutput, ActionInput, ActionKwargs


@register(PostSerializer)
class PostAdmin(ModelAdmin):

    @action(
        name="publish",
        label="Publish Selected",
        output=ActionOutput.Toast,
        confirm=True,
        batch=True,
    )
    async def publish(self, ctx: ActionKwargs) -> str:
        # ctx.ids contains selected record IDs
        await Post.filter(id__in=ctx.ids).update(published=True)
        return "Published successfully"

@action parameters:

Parameter Type Default Description
name str method name Action identifier.
label str method name Display label in the UI.
output ActionOutput Toast Output type: Toast, Download, Redirect, etc.
input ActionInput Empty Input type: Empty, Text, Form, etc.
confirm bool False Show a confirmation dialog before executing.
batch bool False Enable batch selection.
description str "" Tooltip description.

Permissions

By default, BaseAdmin grants access only to superusers. To use RBAC-based permissions, have your admin classes inherit from AuthMixin (from unfazed.contrib.auth.mixin):

from unfazed.contrib.auth.mixin import AuthMixin
from unfazed.contrib.admin.registry import ModelAdmin, register


@register(PostSerializer)
class PostAdmin(AuthMixin, ModelAdmin):
    ...

This checks has_view_permission, has_change_permission, etc. against the user's roles and permissions.

AdminWakeup Lifespan

AdminWakeup is a lifespan class that runs on startup. It iterates over all installed apps and calls app.wakeup("admin"), which imports each app's admin.py module, triggering the @register decorators and populating the admin_collector.

Add it to your LIFESPAN setting:

UNFAZED_SETTINGS = {
    ...
    "LIFESPAN": [
        "unfazed.contrib.admin.registry.lifespan.AdminWakeup",
    ],
}