Skip to main content

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


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

openadmin-0.2.0.tar.gz (993.8 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

openadmin-0.2.0-py3-none-any.whl (33.7 kB view details)

Uploaded Python 3

File details

Details for the file openadmin-0.2.0.tar.gz.

File metadata

  • Download URL: openadmin-0.2.0.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

Hashes for openadmin-0.2.0.tar.gz
Algorithm Hash digest
SHA256 a4b89faed0b54b6f5f9b0f0a2c6edbf6becf5553c4afd4b7860ba7a6529761c6
MD5 155ebe2b226c5a5a0d175484650997ff
BLAKE2b-256 c57b5b051c0af15002c8735efc8e583cca866f371b351b2181366a1498c0001b

See more details on using hashes here.

Provenance

The following attestation bundles were made for openadmin-0.2.0.tar.gz:

Publisher: publish.yml on openadmin-team/openadmin-py

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file openadmin-0.2.0-py3-none-any.whl.

File metadata

  • Download URL: openadmin-0.2.0-py3-none-any.whl
  • Upload date:
  • Size: 33.7 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for openadmin-0.2.0-py3-none-any.whl
Algorithm Hash digest
SHA256 b494d164b573893c256d352dd386ee606160d461dcbb2e7bfe0ad2f4122f6c9e
MD5 bbf6e6d973cd4366d865fc97a1f7c9c3
BLAKE2b-256 5ddb68a9b2e98bd046bf1d393be29a8e4d8c9aae843737917a0d3320da60f6ea

See more details on using hashes here.

Provenance

The following attestation bundles were made for openadmin-0.2.0-py3-none-any.whl:

Publisher: publish.yml on openadmin-team/openadmin-py

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page