Auto-generated admin UI for HawkAPI + hawkapi-sqlalchemy models. Drop your model classes in and get list / detail / create / edit / delete views mounted under /admin — no boilerplate, no React, no JSON-schema duplication.
pip install hawkapi-adminfrom hawkapi import HawkAPI
from hawkapi_sqlalchemy import Base, TimestampMixin, init_database
from sqlalchemy.orm import Mapped, mapped_column
from hawkapi_admin import Admin, ModelResource, init_admin
class User(Base, TimestampMixin):
__tablename__ = "users"
id: Mapped[int] = mapped_column(primary_key=True, autoincrement=True)
email: Mapped[str] = mapped_column(unique=True)
name: Mapped[str] = mapped_column(default="")
app = HawkAPI()
init_database(app, url="postgresql+asyncpg://…/myapp")
admin = init_admin(app, title="My App")
admin.register(User) # simplest formVisit /admin — you get the index page, /admin/user (list), /admin/user/new (create form), /admin/user/{id} (detail), /admin/user/{id}/edit, and POST /admin/user/{id}/delete. All with sane widgets picked from each column's SQLAlchemy type.
from hawkapi_admin import ModelResource
admin.register(ModelResource(
model=User,
label="Account",
label_plural="Accounts",
icon="👤",
list_display=("id", "email", "created_at"),
list_search=("email", "name"),
form_fields=("email", "name"),
readonly_fields=("created_at",),
page_size=25,
can_delete=False,
))| Option | Default | Effect |
|---|---|---|
name |
lowercased class name | URL slug (/admin/<name>) |
label |
class name | Heading on detail / form |
label_plural |
label + "s" |
Nav label & list-page heading |
icon |
"" |
Prepended to nav label |
list_display |
every column | Columns shown on the list page |
list_search |
() |
Columns searched by ?q=... (LIKE) |
form_fields |
every non-PK column | Columns shown in the form |
readonly_fields |
() |
Columns rendered but not editable |
page_size |
50 |
Rows per list page |
can_create / can_update / can_delete |
True |
Toggles the corresponding routes off |
hawkapi-admin picks an input widget per column type, automatically:
bool→ checkboxint/float→<input type="number">date/datetime→ matching native inputString(length > 500)→ textareaEnum→<select>with the declared choices- everything else →
<input type="text">
The plugin doesn't ship its own auth — it picks up whatever middleware you already attached. Combine with hawkapi-auth and gate the admin prefix:
from hawkapi_auth import requires_scopes
admin = Admin(title="Admin", url_prefix="/admin")
admin.register(User)
admin.attach(app)
@app.middleware("http")
async def gate_admin(request, call_next):
if request.url.path.startswith("/admin"):
# raises 401/403 if the JWT is missing / lacks the scope
await requires_scopes("admin:read")(request)
return await call_next(request)The bundled CSS is roughly 60 lines, prefers system colors (light + dark mode), and lives inline in _base.html — copy that template into your own templates/ directory if you want to restyle. Jinja extends "_base.html" keeps working as long as the same blocks (title, content) are defined.
git clone https://github.com/Hawk-API/hawkapi-admin.git
cd hawkapi-admin
uv sync --extra dev
uv run pytest -q
uv run ruff check . && uv run ruff format --check .
uv run pyright src/MIT.