Custom Admin Views

Register standalone pages in the admin namespace with AdminSite.register_view(). Use this for dashboards, reports, or any view outside standard CRUD.

Basic registration

from fastapi import Request
from fastapi.responses import HTMLResponse


async def dashboard(request: Request) -> HTMLResponse:
    return HTMLResponse("""
    <html>
    <head><title>Dashboard</title></head>
    <body>
      <h1>Admin Dashboard</h1>
      <a href="/admin/products/">Products</a>
    </body>
    </html>
    """)


site.register_view("dashboard", "/dashboard/", dashboard)

Accessible at: /admin/dashboard/

Parameters

site.register_view(
    name: str,           # Route name (FastAPI internal)
    path: str,           # URL path relative to admin prefix
    endpoint: Callable,   # Async or sync handler
    permission: Callable[[Request, Any], bool] | None = None,
)

Permission callback

Gate access to custom views:

def admin_only(request: Request, user) -> bool:
    return bool(user and user.get("role") == "admin")

site.register_view("reports", "/reports/", reports_view, permission=admin_only)

Using admin styles

Link bundled admin CSS for consistent styling:

async def dashboard(request: Request) -> HTMLResponse:
    html = """
    <!DOCTYPE html>
    <html lang="en" data-theme="light">
    <head>
      <link rel="stylesheet" href="/admin/static/admin.css">
    </head>
    <body>
      <header class="base-header admin-header">
        <div class="base-header__brand">
          <a href="/admin/">My Admin</a>
        </div>
      </header>
      <main class="base-card admin-main">
        <h1>Dashboard</h1>
      </main>
    </body>
    </html>
    """
    return HTMLResponse(content=html)

Returning Jinja2 templates

For full template integration, render Jinja2 manually or return an HTMLResponse with rendered content. Custom views are not automatically wrapped in the admin base layout — you control the full HTML.

Model-specific URLs

ModelAdmin.get_urls() registers extra routes under a model prefix:

class ProductAdmin(ModelAdmin):
    def get_urls(self) -> list[tuple[str, Any]]:
        return [
            ("export/", self.export_view),
        ]

    async def export_view(self, request: Request) -> Response:
        ...

Mounted at: /admin/products/export/

Ecommerce dashboard example

The demo registers a custom dashboard at /admin/dashboard/:

site.register_view("dashboard", "/dashboard/", ecommerce_dashboard)

See example/ecommerce/admin.py for the full implementation.