LiteAdmin
Django Admin–style admin panel for Litestar and FastAPI, with Tortoise ORM and SQLAlchemy.
React UI ships in litedmin/static/ — no npm needed for normal use.
Install
pip install litedmin
pip install "litedmin[fastapi,sqlalchemy]" # FastAPI + SQLAlchemy
pip install "litedmin[litestar,tortoise]" # Litestar + Tortoise
pip install "litedmin[all]" # both ORMs + frameworks + dev tools
Core symbols (AdminConfig, OrmSettings, register, ModelAdmin) import without ORM/framework extras.
ORM and web stacks load only when you import their modules (or touch lazy attributes on litedmin).
From source (examples / development)
git clone https://github.com/ggb0yyyyyyy/litedmin.git
cd litedmin
pip install -e ".[litestar,tortoise]" # examples/tortoise (port 8000)
pip install -e ".[litestar,sqlalchemy]" # examples/sqlalchemy (port 8011)
pip install -e ".[fastapi,tortoise]"
pip install -e ".[all]"
Repository layout
liteDmin/
litedmin/ installable package
backend/
core/ AdminSite, config, OrmSettings, decorators
admin/ ModelAdmin, changelist, formsets, permissions
api/ AdminAPI / endpoints
auth/ session, backends, UserAdminMixin, system models
orm/ Tortoise / SQLAlchemy backends + repositories
integration/ Litestar & FastAPI Admin mounts, DI helpers
dto/ storage/
static/ bundled React UI
frontend/ React sources (rebuild → litedmin/static/)
examples/ Litestar + Tortoise / SQLAlchemy demos
tests/
Quick start (Litestar + Tortoise)
Superuser is not created on app startup. Create it once via CLI, then run the example:
# optional credentials for CLI
export ADMIN_USERNAME=admin
export ADMIN_PASSWORD=admin
py -3.11 -m litedmin createsuperuser --settings examples.tortoise.settings:orm_settings
py -3.11 -m uvicorn examples.tortoise.app:app --host 0.0.0.0 --port 8000
Open http://localhost:8000/admin
SQLAlchemy example: same flow with examples.sqlalchemy.settings:orm_settings and port 8011.
Details: examples/README.md, examples/CREDENTIALS.md.
Minimal wiring
from litestar import Litestar, get
# Shared Starlette Admin ASGI app; Litestar/FastAPI only provide mount helpers.
from litedmin.backend.integration.litestar import Admin
from litedmin.backend.integration import init_tortoise_with_litedmin, close_tortoise
# FastAPI: from litedmin.backend.integration.fastapi import Admin
# or: from litedmin import Admin
# Then: app.mount(admin.prefix, admin.as_asgi_app())
from examples.tortoise.admin_config import ExampleAdminConfig
from examples.tortoise.settings import AUTO_DISCOVER_MODULES, orm_settings
admin = Admin(
prefix='/admin',
config=ExampleAdminConfig(), # AdminConfig: theme / UI / auth_user_model
auto_discover_modules=AUTO_DISCOVER_MODULES,
)
async def on_startup():
await init_tortoise_with_litedmin(orm_settings) # OrmSettings: ORM + DB
await admin.ensure_started() # validate admin + sync permissions (no user seed)
async def on_shutdown():
await close_tortoise()
@get('/')
async def root() -> dict:
return {'admin': '/admin'}
app = Litestar(
route_handlers=[root, admin.as_mount()],
on_startup=[on_startup],
on_shutdown=[on_shutdown],
)
File / rich-text media storage
Pass any StorageBackend (save / delete / url) into Admin(storage=...). Rich editor uploads go to POST /admin/media/upload and embed the returned url in HTML.
Examples use LocalFileStorage + /media static route — see examples/README.md. Swap in S3/MinIO by implementing the same protocol in your app.
SQLAlchemy apps also call admin.configure_sqlalchemy(...) after init_sqlalchemy_with_litedmin_async — see examples/sqlalchemy/app.py.
ModelAdmin
- Prefer
TortoiseModelAdmin/SQLAlchemyModelAdmin(repository save/delete). - Register with
@register/AdminSite.register—admin_siteis always injected; do not construct ModelAdmin without a site. - Permission / persist hooks are
async(has_*,get_permissions,get_list_scope_filters,save_model,delete_model,process_extra_fields). Sync getters (get_list_display, …) stay sync. - Users with password field:
UserAdminMixin. - Long readonly textareas: pass options on the widget itself (SPA preview + “Show full text” dialog):
from litedmin import Textarea
class AnalysisAdmin(SQLAlchemyModelAdmin):
widgets = {
'error': Textarea(collapse_chars=280),
}
Field references (SQLAlchemy)
Settings accept plain names ('title', 'author__username') for any ORM. For SQLAlchemy you can also pass the model's own attributes (and Link(...) for relation paths), so a type checker catches a misspelt field:
from litedmin import Link, action, display, register
from litedmin.backend.orm.sqlalchemy import SQLAlchemyModelAdmin
@register(Post)
class PostAdmin(SQLAlchemyModelAdmin):
list_display = [Post.id, Post.title, Link(Post.author, User.username), 'title_upper']
search_fields = [Post.title, Link(Post.author, User.email)]
ordering = [Post.created_at.desc()]
list_filter = [Post.published, Post.status]
Tortoise models have no field attributes on the class — keep using strings there ('title', 'author__username'). Computed @display columns are always strings.
Tortoise example
from litedmin import action, register, display
from litedmin.backend.orm.tortoise import TortoiseModelAdmin
@action(description='Publish selected', row=True)
async def make_published(modeladmin, request, objects):
# ``objects`` is a list of instances (not an ORM queryset)
...
# Optional: return a status string for the SPA toast (e.g. background job accepted)
# return 'Export queued'
@action(description='Refresh cache', toolbar=True, bulk=False, row=False)
async def refresh_cache(modeladmin, request, objects):
# Page toolbar button (no selection); same POST /{model}/action with ids=[]
...
return 'Refresh queued'
@register(Post)
class PostAdmin(TortoiseModelAdmin):
list_display = ['title', 'title_upper']
actions = [make_published, refresh_cache] # each entry must use @action
@display(label='Upper title')
def title_upper(self, obj):
return obj.title.upper()
async def has_export_permission(self, request=None, obj=None):
return True
List actions require @action (Django @admin.action style). Use row=True for per-row buttons; bulk=False to hide from the bulk dropdown; toolbar=True for page-level tools above the list (always empty selection). Signature is always (modeladmin, request, objects).
Auth
- Default: session cookie login (
SessionAuthBackend+SessionAuth). - SSO (Keycloak gateway handoff):
GatewayWebAuthBackend— seeexamples/tortoise/app_gateway_web.pyand examples/README.md.
CLI
litedmin createsuperuser --settings examples.tortoise.settings:orm_settings
# or
python -m litedmin createsuperuser --settings module.path:orm_settings
--username / --password / --email, or ADMIN_USERNAME / ADMIN_PASSWORD / ADMIN_EMAIL.
Config split
| Object | Role |
|---|---|
AdminConfig |
Theme, UI, sidebar groups, optional auth_user_model |
OrmSettings |
orm_type, database URL, app modules for ORM init |
Features
@register/AdminSite.registerModelAdmin API- SQLAlchemy field attributes /
Link(...)in ModelAdmin settings (strings for Tortoise) @displaycomputed list columns@actionlist actions (bulk dropdown + optionalrow=Truebuttons)- CSV/JSON export (
has_export_permission) - Django-like permissions, groups, LogEntry audit trail
list_editable,date_hierarchy, bulk actions, inlines- Row-level scope via
get_list_scope_filters+ object-levelhas_* - Double-submit CSRF cookie for session auth (
AdminConfig.csrf_protect) StorageBackendprotocol for file uploads
Typing
Package is typed; project mypy config is in pyproject.toml ([tool.mypy]).
pip install -e ".[dev]"
python -m mypy litedmin
Change the React UI
pip install -e ".[all]"
# or: pip install -r requirements-dev.txt
bash scripts/build_frontend.sh
git add litedmin/static/
License
MIT
Metadata
Release files for litedmin 0.4.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | Actions |
|---|---|---|---|---|
| litedmin-0.4.0.tar.gz | 2.5 MB | Without provenance | Details for litedmin-0.4.0.tar.gz |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Actions Reset |
|---|---|---|---|---|
| litedmin-0.4.0-py3-none-any.whl | Python 3 | none | any | Details for litedmin-0.4.0-py3-none-any.whl |
Total release size: 5.1 MB
Release files / litedmin-0.4.0.tar.gz
| Download URL | litedmin-0.4.0.tar.gz |
|---|---|
| Size | 2.5 MB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
54ba72b08548dcd02cdb1f637b74c5680ec91cd8e856aec9d10d4fac05d4e5e9
|
|
BLAKE2b-256 checksum How to use checksums |
4ef9744c02933ec7e78fcd4f57ffa21f59c926eac49e525727c353a4beb0f1c9
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.14.4
|
Release files / litedmin-0.4.0-py3-none-any.whl
| Download URL | litedmin-0.4.0-py3-none-any.whl |
|---|---|
| Size | 2.6 MB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
d45ecfbfdd43b9270cc008317dd8a5b95753231020bfc3f33d7f9df6f68ffa69
|
|
BLAKE2b-256 checksum How to use checksums |
4467f22bc2de9e36163fc31378765b32d544e4eee8d39ca77ae2fa74e141df10
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.14.4
|
Security for litedmin 0.4.0
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
No provenance found
None of the files in this release were uploaded with provenance metadata.
A note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.
To learn more about protecting yourself from malicious actors, visit our package security documentation.