Add your description here
Project description
🚧 Project is under development 🚧
OpenAdmin is a FastAPI-native library for building admin dashboards. Define pages with typed decorators — no frontend code, no templates, no configuration files. Every page is just a router; every widget is just an endpoint
Features
- Stats — display single values: counts, totals, booleans, percentages
- Tables — paginated, searchable data grids with per-row actions
- Charts — area, bar, line, and pie charts with labeled series
- Actions — trigger HTTP calls (GET / POST / PUT / PATCH / DELETE) from the UI
- Forms — structured forms that POST/PUT/PATCH/DELETE to your own endpoints
- Markdown — render rich text content on any page
- FastAPI-native — full dependency injection, OpenAPI docs, and router composition out of the box
Installation
pip install openadmin-py
# or with uv
uv add openadmin-py
Quick Start
from fastapi import FastAPI
from openadmin.fastapi import AdminPanel, AdminPage
from openadmin.types import Stat, Table
# Create an admin panel (a FastAPI sub-application)
admin = AdminPanel()
# Define a page
page = AdminPage("Dashboard")
@page.stat("Total Users")
async def total_users() -> Stat:
return Stat(value=1_024)
@page.table("Recent Users")
async def recent_users() -> Table:
return Table(data=[
{"id": 1, "name": "Alice", "role": "admin"},
{"id": 2, "name": "Bob", "role": "viewer"},
])
# Register the page and mount the panel
admin.include_page(page)
app = FastAPI()
app.mount("/admin", admin)
Usage
Stats
Display a single numeric or boolean value.
from openadmin.types import Stat
@page.stat("Active Sessions")
async def active_sessions() -> Stat:
return Stat(value=42)
Tables
Paginated tables with optional search. Use the built-in dependency types to receive pagination and search parameters automatically.
from openadmin.fastapi import PaginationParamsDep, SearchQueryDep
from openadmin.types import Table
@page.table("Users")
async def users_table(
pagination: PaginationParamsDep,
search: SearchQueryDep,
) -> Table:
# pagination.page, pagination.per_page
# search is str | None
return Table(data=[...])
Row Actions
Attach per-row action buttons by including __actions__ in each row:
from openadmin.types import Action, Table, TableRow
@page.table("Users")
async def users_table() -> Table:
return Table(data=[
TableRow(
id=1,
name="Alice",
__actions__=[
Action(color="danger", method="DELETE", url="/users/1", body=None),
Action(color="info", method="POST", url="/users/1/reset", body=None),
],
)
])
Charts
All chart types share the same structure: a data list of dicts and a config that maps series keys to display labels and colors.
from openadmin.types import BarChart, PieChart
@page.bar_chart("Sales by Region", "Total sales per region this quarter")
async def sales_chart() -> BarChart:
return BarChart(
data=[
{"region": "North", "sales": 120},
{"region": "South", "sales": 95},
],
config={"sales": {"label": "Sales", "color": "#6366f1"}},
)
@page.pie_chart("User Roles", "Breakdown of user roles")
async def roles_chart() -> PieChart:
return PieChart(
data=[
{"segment": "Admin", "count": 5},
{"segment": "Editor", "count": 20},
{"segment": "Viewer", "count": 75},
],
config={"count": {"label": "Users", "color": "#10b981"}},
)
Actions
Expose buttons that trigger HTTP requests — useful for one-off operations like cache clearing or triggering background jobs.
@page.action_post("Clear Cache")
async def clear_cache():
# your logic here
return {"status": "cleared"}
Forms
Forms collect user input and submit it to your endpoint. Mark a form hidden if it should not appear in the page navigation.
from pydantic import BaseModel
class InvitePayload(BaseModel):
email: str
role: str
@page.form_post("Invite User", "Send an invitation email to a new user")
async def invite_user(payload: InvitePayload):
# send invite...
return {"invited": payload.email}
Markdown
Render markdown text directly on a page — useful for instructions, changelogs, or documentation sections.
@page.markdown("Release Notes")
async def release_notes() -> str:
return "## v1.2.0\n- Added dark mode\n- Fixed pagination bug"
Full Example
A real-world page querying a SQLAlchemy database:
from sqlalchemy import func, select
from openadmin.fastapi import AdminPage, PaginationParamsDep, SearchQueryDep
from openadmin.types import Stat, Table, BarChart
page = AdminPage("Library")
@page.stat("Total Books")
async def total_books(session: AsyncSessionDep) -> Stat:
result = await session.execute(select(func.count()).select_from(Book))
return Stat(value=result.scalar_one())
@page.table("Books")
async def books_table(
session: AsyncSessionDep,
pagination: PaginationParamsDep,
search: SearchQueryDep,
) -> Table:
stmt = select(Book).offset(pagination.page * pagination.per_page).limit(pagination.per_page)
books = (await session.execute(stmt)).scalars().all()
return Table(data=[{"title": b.title, "year": b.published_year} for b in books])
@page.bar_chart("Books per Genre", "Number of books in each genre")
async def books_per_genre(session: AsyncSessionDep) -> BarChart:
rows = (await session.execute(
select(Genre.name, func.count().label("books"))
.join(BookToGenre).group_by(Genre.id)
)).all()
return BarChart(
data=[{"genre": name, "books": count} for name, count in rows],
config={"books": {"label": "Books", "color": "#6366f1"}},
)
See the examples/ directory for a complete runnable application.
make dev/run
# → http://127.0.0.1:8000/docs
API Reference
AdminPanel
A FastAPI subclass. Use include_page(page) to register an AdminPage.
| Method | Description |
|---|---|
include_page(page, tags=None) |
Mount an AdminPage onto the panel |
AdminPage
An APIRouter subclass. Each decorator creates a typed GET (or POST/PUT/etc.) endpoint under the page's prefix.
| Decorator | HTTP | Response type |
|---|---|---|
@page.stat(name) |
GET | Stat |
@page.table(name) |
GET | Table |
@page.markdown(name) |
GET | str |
@page.area_chart(name, description) |
GET | AreaChart |
@page.bar_chart(name, description) |
GET | BarChart |
@page.line_chart(name, description) |
GET | LineChart |
@page.pie_chart(name, description) |
GET | PieChart |
@page.action_get/post/put/patch/delete(name) |
* | any |
@page.form_post/put/patch/delete(name, description) |
* | any |
Dependencies
| Name | Type | Description |
|---|---|---|
PaginationParamsDep |
PaginationParams |
page and per_page query params |
SearchQueryDep |
str | None |
search query param |
Types
| Type | Fields |
|---|---|
Stat |
value: str | bool | int | float |
Table |
data: list[TableRow | dict] |
TableRow |
any fields + __actions__: list[Action] |
Action |
color, method, url, body |
AreaChart / BarChart / LineChart / PieChart |
data: list[dict], config: dict |
Development
# Run the example app
make dev/run
# Run all checks (format, lint, types, tests, security)
make check
# Auto-fix formatting and lint issues
make fix
License
AGPL-3.0-or-later — © 2026 OpenAdmin
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file openadmin_dev-0.3.1.tar.gz.
File metadata
- Download URL: openadmin_dev-0.3.1.tar.gz
- Upload date:
- Size: 993.8 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
93fbf765959e25f05da67887f56eb9fb7f9b4e8e61cbfa58c959460bc3965cf3
|
|
| MD5 |
921b9a7d17cf31d42c67a46bb4e8c9e7
|
|
| BLAKE2b-256 |
aaa1c0b3b42974988ea12bec145a93973ef78a5a76eb66b25eabfd418aa41d93
|
Provenance
The following attestation bundles were made for openadmin_dev-0.3.1.tar.gz:
Publisher:
publish.yml on openadmin-team/openadmin-py
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
openadmin_dev-0.3.1.tar.gz -
Subject digest:
93fbf765959e25f05da67887f56eb9fb7f9b4e8e61cbfa58c959460bc3965cf3 - Sigstore transparency entry: 1881993838
- Sigstore integration time:
-
Permalink:
openadmin-team/openadmin-py@a51489c59084dd82b11cf7f9b3f1f8aac1185427 -
Branch / Tag:
refs/heads/dev - Owner: https://github.com/openadmin-team
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@a51489c59084dd82b11cf7f9b3f1f8aac1185427 -
Trigger Event:
push
-
Statement type:
File details
Details for the file openadmin_dev-0.3.1-py3-none-any.whl.
File metadata
- Download URL: openadmin_dev-0.3.1-py3-none-any.whl
- Upload date:
- Size: 33.4 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
5d46cfdbff5f5fd2a2b4d61be1b8f2049f4479622166b7ab31414270e7b499f9
|
|
| MD5 |
dbcc0fb98e65aa469d7b43bb0a9bfa7d
|
|
| BLAKE2b-256 |
0b92e93120c0522a194f6331573325eb1b781eb764371e2cfbefea9d5e59b23a
|
Provenance
The following attestation bundles were made for openadmin_dev-0.3.1-py3-none-any.whl:
Publisher:
publish.yml on openadmin-team/openadmin-py
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
openadmin_dev-0.3.1-py3-none-any.whl -
Subject digest:
5d46cfdbff5f5fd2a2b4d61be1b8f2049f4479622166b7ab31414270e7b499f9 - Sigstore transparency entry: 1881994000
- Sigstore integration time:
-
Permalink:
openadmin-team/openadmin-py@a51489c59084dd82b11cf7f9b3f1f8aac1185427 -
Branch / Tag:
refs/heads/dev - Owner: https://github.com/openadmin-team
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@a51489c59084dd82b11cf7f9b3f1f8aac1185427 -
Trigger Event:
push
-
Statement type: