Überwachte maschinelle Übersetzung
Dieser Inhalt wurde maschinell übersetzt und basiert auf von Menschen kuratierten Glossaren und Styleguides. Da der Text nicht zeilenweise manuell überprüft wird, können gelegentlich Fehler oder unklare Formulierungen auftreten.
Bei etwaigen Abweichungen ist die ursprüngliche englische Version die maßgebliche Quelle.
Custom Views
Nicht jede Admin-Seite lässt sich auf ein Datenbankmodell abbilden. Ein CustomView erzeugt eine eigenständige Seite in der Sidebar, die Sie selbst aus integrierten Widgets, eigenen Templates oder eigenen Routen zusammenstellen.
In den meisten Fällen müssen Sie nichts ableiten. Instanziieren Sie CustomView, übergeben Sie ein Widget und registrieren Sie es:
from starlette.requests import Request
from starlette_admin import CustomView, StatWidget
from starlette_admin.contrib.sqla import Admin
async def count_pending_jobs(request: Request) -> int:
return 3
admin = Admin(engine, title="My Admin", secret_key="change-me")
admin.add_view(
CustomView(
menu_label="System Status",
icon="fa fa-heart-pulse",
path="/status",
widget=StatWidget(
title="Pending background jobs", value_callback=count_pending_jobs
),
)
)
menu_label,iconundpath: Steuern den Sidebar-Eintrag und die URL.widget: Legt fest, was die Seite rendert. Übergeben Sie eine einzelneBaseWidget-Instanz oder eine aufrufbare Funktion ((request) -> BaseWidget | None), die den Widget-Baum pro Request aufbaut. Verwenden Sie die aufrufbare Form, wenn die Seite von Live-Daten, dem aktuellen Benutzer oder Feature-Flags abhängt.
Um mehr als ein Widget anzuzeigen, übergeben Sie ein Layout-Widget, das Kinder enthält.
Leiten Sie CustomView nur dann ab, wenn Sie eigene Endpoints, Zugriffskontrolle oder volle Kontrolle über die HTTP-Antwort benötigen.
Content-Widgets
Content-Widgets rendern die Daten selbst. Ihre *_callback-Parameter akzeptieren asynchrone Callables, die den aktuellen Request erhalten, sodass jedes Widget Live-Daten abrufen kann.
Die vollständige Konstruktorsignatur aller unten aufgeführten Widgets finden Sie in der Widgets-API-Referenz.
StatWidget
Eine KPI-Karte, die eine einzelne Metrik, eine optionale Beschreibung und einen Sparkline-Chart anzeigt.
from starlette.requests import Request
from starlette_admin import StatWidget
async def count_orders(request: Request) -> int:
session = request.state.session
return await session.scalar(select(func.count(Order.id)))
orders_stat = StatWidget(
title="Total Orders",
value_callback=count_orders,
description="This month",
color="success",
)
Wichtige Parameter:
titleundvalue_callback: Das Label und das asynchrone Callable, das die Metrik zurückgibt.descriptionundcolor: Der sekundäre Text unterhalb des Werts.colorakzeptiert Tabler-Farb-Tokens wie"success"und"danger".link: Umschließt die gesamte Karte mit einem Anker.chart_callback: Gibt eine ApexCharts-series-Liste zurück, z. B.[{"name": "Views", "data": [10, 20, 30]}], um am unteren Rand der Karte einen Sparkline zu rendern.countup: Animiert den Metrikwert beim Laden mit countup.js.
ChartWidget
Ein ApexCharts-Diagramm innerhalb einer Karte.
from starlette.requests import Request
from starlette_admin import ChartWidget
async def revenue_series(request: Request) -> list[dict]:
return [{"name": "Revenue", "data": [1200, 1450, 1100, 1800]}]
revenue_chart = ChartWidget(
title="Revenue over time",
chart_type="line",
series_callback=revenue_series,
height=300,
)
Wichtige Parameter:
chart_type: Jeder gültige ApexCharts-String, z. B."line","bar","pie","donut"oder"heatmap".series_callback: Gibt für die meisten Diagrammtypen eine Liste von Dictionaries zurück ([{"name": "...", "data": [...]}]). Für"pie","donut"und"radialBar"gibt sie eine flache Liste von Zahlen zurück.options: Ein Dictionary, das über die Standardkonfiguration von ApexCharts gemischt wird. Verwenden Sie es für typspezifische Einstellungen wiexaxis.categoriesoderlabels.
TableWidget
Eine kompakte, schreibgeschützte Übersichtstabelle.
from starlette.requests import Request
from starlette_admin import TableWidget
async def recent_orders(request: Request) -> list[list]:
return [["#1042", "Ada Lovelace", 129.00], ["#1041", "Alan Turing", 89.50]]
latest_orders = TableWidget(
title="Latest Orders",
columns=["Order", "Customer", "Total"],
rows_callback=recent_orders,
)
TextWidget & HtmlWidget
Verwenden Sie TextWidget für reinen Text oder Markdown und HtmlWidget für bereits gerenderte HTML-Blöcke.
from starlette_admin import TextWidget, HtmlWidget
notes = TextWidget(
content="## Release notes\n\n- Filters now support date ranges",
markdown=True, # Requires `pip install markdown`
card=True, # Set to False to render without the surrounding card
)
banner = HtmlWidget(
html='<div class="alert alert-info">Maintenance window at 22:00 UTC</div>'
)
Sicherheitsrisiko
HtmlWidget rendert Strings exakt so, wie Sie sie bereitstellen, ohne Escape-Mechanismus. Übergeben Sie niemals benutzerdefinierten Inhalte daran weiter, da dies Ihr Admin-Panel XSS-Injektionen aussetzt.
DividerWidget
Rendert eine horizontale Trennlinie (<hr>), um Abschnitte voneinander zu trennen. Es nimmt keine Parameter entgegen.
Layout-Widgets
Layout-Widgets bilden das strukturelle Grundgerüst Ihrer Custom Views. Statt Daten zu rendern, organisieren, ausrichten und positionieren sie Ihre Content-Widgets.
Note
Dieselben Layout-Widgets treiben auch das Attribut form_layout auf ModelView an, sodass Sie damit auch Create- und Edit-Formularfelder in Spalten, Panels und Tabs anordnen können. Siehe Form Layouts.
Rows, Columns und Cards
Um ein responsives Grid zu bauen, kombinieren Sie diese Layout-Primitives:
ColumnWidget: Das vertikale Fundament. Es stapelt Kind-Widgets von oben nach unten und dient üblicherweise als Root-Container für Ihren Dashboard-Baum.RowWidget: Der horizontale Container. Er ordnet seine Kinder nebeneinander in einer Flexbox-Zeile an. Umhüllen Sie ein Kind mit einemCol-Objekt, um dessen responsive Breite überBreakpointsauf einem Standardraster von 1–12 festzulegen. Nicht umhüllte Kinder teilen sich die verfügbare Breite gleichmäßig.CardRowWidget: Die ausgefeilte Zeile. Es erbt sämtliche Mechaniken vonRowWidgetund gibt allen Kindern gleiche Höhen. Verwenden Sie es, wenn Sie eine Reihe von Karten anordnen, etwa KPI-Statistiken, Charts oder Datentabellen.
from starlette_admin import Breakpoints, CardRowWidget, Col, ColumnWidget
# Group statistics side-by-side in equal-height cards
kpi_row = CardRowWidget(
children=[
Col(orders_stat, breakpoints=Breakpoints(default=12, md=6)),
Col(revenue_stat, breakpoints=Breakpoints(default=12, md=6)),
]
)
# Stack the KPI row above the charts and tables
page = ColumnWidget(children=[kpi_row, revenue_chart, latest_orders])
GridWidget
Ein responsives Grid, das auf Bootstraps row-cols-*-System aufbaut. Anders als bei Col legt Breakpoints hier die Anzahl der Elemente pro Zeile fest, nicht die Column-Spanne.
from starlette_admin import Breakpoints, GridWidget
stats_grid = GridWidget(
children=[orders_stat, revenue_stat, users_stat],
breakpoints=Breakpoints(default=1, md=2, lg=3),
gutter=3, # Bootstrap gutter scale (0-5)
)
PanelWidget & TabsWidget
PanelWidget: Umschließt Kinder in einer betitelten Karte, die Sie einklappbar machen können.TabsWidget: Rendert pro Tab ein Widget.tabsakzeptiert eine Liste von(label, widget)-Tupeln.
from starlette_admin import PanelWidget, TabsWidget
orders_panel = PanelWidget(
title="Recent Orders",
icon="fa-solid fa-clock",
children=[latest_orders, notes],
collapsible=True,
)
reports = TabsWidget(tabs=[("Revenue", revenue_chart), ("Orders", latest_orders)])
Das Home-Dashboard zusammenstellen
Ein CustomView versorgt den Standard-Admin-Root (/admin/). Wenn Sie keinen angeben, erstellt Admin eine DefaultIndexView, die Record-Anzahlen und Links für Ihre registrierten Modelle anzeigt.
Um Ihr eigenes Dashboard zu bauen, schreiben Sie eine Funktion, die den Widget-Baum zusammensetzt, und übergeben Sie sie an den Parameter index_view Ihrer Admin-Instanz.
async def build_dashboard(request: Request) -> ColumnWidget:
return ColumnWidget(
children=[
RowWidget(...),
ChartWidget(...),
PanelWidget(...),
]
)
admin = Admin(
engine,
title="My Admin",
secret_key="change-me",
index_view=CustomView(
menu_label="Dashboard",
icon="fa fa-home",
widget=build_dashboard,
),
)
Da build_dashboard bei jedem Request ausgeführt wird, kann sich Ihr Layout zur Laufzeit anpassen. So können Sie beispielsweise Panels für Nicht-Admins ausblenden oder Charts austauschen.
Eigene Templates
Wenn das Widget-System nicht flexibel genug ist, leiten Sie CustomView ab und dekorieren Sie index mit @route("") neu, um Ihr eigenes Jinja-Template statt des standardmäßigen Widget-Renderings zu rendern:
from starlette_admin import CustomView, route
class StatusView(CustomView):
menu_label = "System Status"
path = "/status"
@route("")
async def index(self, request: Request) -> Response:
return self.templates.TemplateResponse(
request=request,
name="status.html",
context={"title": self.title(request)},
)
admin.add_view(StatusView())
self.templates ist eine Jinja2Templates-Instanz, die gegen Ihr Templates-Verzeichnis aufgelöst wird. Sie steht erst nach dem Mounten des Views zur Verfügung – rufen Sie sie daher nicht in __init__ auf. Ein eigenes Template erweitert üblicherweise das Basis-Layout des Admin-Panels:
{% extends "layout.html" %}
{% block content %}
<h1>System status</h1>
<p>All services operational.</p>
{% endblock %}
Um Widgets innerhalb Ihres eigenen Templates anzuzeigen, lösen und rendern Sie diese selbst auf und übergeben das Ergebnis über Ihren eigenen Kontext:
from starlette_admin.widgets import render_widget
class StatusView(CustomView):
menu_label = "System Status"
path = "/status"
widget = StatWidget(title="Pending jobs", value_callback=count_pending_jobs)
@route("")
async def index(self, request: Request) -> Response:
widget = await self._resolve_widget(request)
return self.templates.TemplateResponse(
request=request,
name="status.html",
context={
"title": self.title(request),
"widget_html": await render_widget(widget, request, self.templates.env),
"widget_additional_css": widget.additional_css_links(request),
"widget_additional_js": widget.additional_js_links(request),
},
)
{% extends "layout.html" %}
{% block head_css %}
{{ super() }}
{% for link in widget_additional_css %}<link rel="stylesheet" href="{{ link }}">{% endfor %}
{% endblock %}
{% block content %}
<h1>System status</h1>
{{ widget_html }}
{% endblock %}
{% block script %}
{{ super() }}
{% for link in widget_additional_js %}<script src="{{ link }}"></script>{% endfor %}
{% endblock %}
Important
Lassen Sie die CSS- und JS-Blöcke nur dann weg, wenn Ihr Template keine Widgets rendert. Ohne diese Blöcke können Chart- und Stat-Widgets ihre Abhängigkeiten wie ApexCharts nicht laden.
Routen mit @route hinzufügen
Wenn eine eigene Seite eigene Endpoints benötigt – etwa eine JSON-Route, die ein clientseitiges Chart speist, oder einen POST-Handler für ein Formular –, leiten Sie CustomView ab und hängen Methoden mit @route an:
from starlette.responses import JSONResponse
from starlette_admin import CustomView, route
class ReportsView(CustomView):
menu_label = "Reports"
icon = "fa fa-file-lines"
path = "/reports"
@route("")
async def index(self, request: Request) -> Response:
return self.templates.TemplateResponse(
request=request,
name="reports/index.html",
context={"title": self.title(request)},
)
@route("/data", methods=["GET"])
async def report_list(self, request: Request):
return JSONResponse([{"id": "001", "status": "ready"}])
@route("/generate", methods=["POST"])
async def generate(self, request: Request):
form = await request.form()
return JSONResponse({"status": "queued"})
admin.add_view(ReportsView())
path: Wird an den Root-Pfad des Views angehängt. Im obigen Beispiel registriert@route("/data")unter/admin/reports/data.- CSRF-Schutz: Jede verändernde Route (POST, PUT, DELETE) durchläuft die CSRF-Middleware; Formulare in Ihren Templates müssen daher
{{ csrf_input(request) }}enthalten. CustomViewfällt von Konstruktorargumenten auf Klassenattribute zurück, genau wieModelView. Daher funktioniertReportsView()oben ohne Argumente. Übergeben Sie Overrides, etwaReportsView(menu_label="..."), wenn Sie eine Konfiguration pro Instanz benötigen.
Zugriffskontrolle
Um einen gesamten CustomView zu sperren, überschreiben Sie die Methode is_accessible(request). Gibt sie False zurück, entfernt das Admin-Panel den View aus der Sidebar, und jeder @route-Endpoint gibt 403 Forbidden zurück.
class ReportsView(CustomView):
menu_label = "Reports"
path = "/reports"
def is_accessible(self, request: Request) -> bool:
return request.state.admin_user is not None
Für feingranulare Berechtigungen – etwa wenn jeder die Seite sehen darf, aber nur Admins POST-Anfragen senden dürfen – platzieren Sie diese Logik im jeweiligen @route-Handler statt in is_accessible.
Sehen Sie sich examples/07-dashboard an – eine lauffähige App, die jedes auf dieser Seite beschriebene Widget verwendet:
StatWidget,ChartWidget,TableWidget,TabsWidget,PanelWidgetundGridWidget.
Wie es weitergeht
- Templates: Passen Sie das Basis-Layout an, das Ihre Custom-View-Templates erweitern.
- Flash Messages: Geben Sie Feedback aus Ihren
@route-Handlern aus. - Actions: Fügen Sie Massen- und Zeilenaktionen zu Ihren Model-Views hinzu.