This release is a pre-release and may not be stable for production use.
🚀 SnapAdmin — Declarative Django Admin & API
Describe a model's fields once. Get a full Django admin, a REST API with Swagger docs, a GraphQL endpoint and optional Elasticsearch search — no boilerplate. Every surface is one setting away from being switched off.
📚 Full Documentation · 📦 Django Packages · 📝 Changelog · 🔒 Security policy · 🧭 llms.txt (docs map for AI assistants)
🏁 See it running in 60 seconds
No project, no config, nothing to clone:
pip install django-snapadmin
snapadmin-demo
That downloads a ready-made demo project, migrates it, seeds sample data and serves it on
http://localhost:8000. Log in at /admin/ with admin / admin and click around. Delete
~/.cache/snapadmin-demo/ when you're done.
🧠 The idea, in three steps
1. Declare the model. The Snap*Field kwargs describe how the field should behave — in the
admin, in the API, in search. They add no database migration; they are stripped before Django
sees the field.
# models.py
from snapadmin import fields as snap, models as snap_models
class Product(snap_models.SnapModel):
name = snap.SnapCharField(max_length=200, searchable=True, show_in_list=True)
price = snap.SnapDecimalField(max_digits=10, decimal_places=2, filterable=True)
available = snap.SnapBooleanField(default=True, filterable=True)
2. Turn the surfaces you want on.
# settings.py
SNAPADMIN_REST_API_ENABLED = True # /api/… CRUD
SNAPADMIN_GRAPHQL_ENABLED = True # /api/graphql/
SNAPADMIN_SWAGGER_ENABLED = True # /api/docs/
3. Register. One line — every SnapModel in the project gets an admin.
# admin.py
from snapadmin.models import SnapModel
SnapModel.register_all_admins()
You now have:
/admin/ |
list view with a search box on name, sidebar filters for price (range) and available, an add/edit form, and field-level change logging |
/api/models/demo/Product/ |
REST list · retrieve · create · update · delete, with filtering, pagination and token auth |
/api/docs/ |
Swagger UI + ReDoc, generated from the same models |
/api/graphql/ |
a Graphene schema with allDemoProducts, permission-checked per relation |
/dashboard/ |
row counts per model, service health, scheduled jobs |
┌────────────────────────────────────────────────────────────┐
│ SnapAdmin 🔍 Search... admin ▾│
├──────────────┬─────────────────────────────────────────────┤
│ SHOP │ Products + Add │
│ Categories │ ┌──────────────────────────────────────────┐│
│ Products │ │ Name Price In Stock Category ││
│ Customers │ │ Premium Laptop $249 ● Active Audio ││
│ Orders │ │ Ergonomic Mouse $89 ● Active Access. ││
│ SYSTEM │ │ USB-C Hub $49 ○ Out Electr. ││
│ Dashboard │ └──────────────────────────────────────────┘│
└──────────────┴─────────────────────────────────────────────┘
→ Field types · SnapModel reference · Admin registration
🧭 How this differs from Unfold, Jazzmin and Grappelli
Those are themes: they restyle the admin you have written. You still write the ModelAdmin —
the list_display, the search_fields, the filters — and they make it look modern.
SnapAdmin generates that admin from your field declarations, and generates the REST API,
the GraphQL schema and the search mapping from the same ones. It is not an alternative theme;
it sits a layer above, and it uses Unfold as its optional theme ([theme] extra). Keep your
theme, keep your hand-written ModelAdmin where you want one — SnapAdmin never replaces an admin
class you registered yourself.
| Themes (Unfold · Jazzmin · Grappelli) | SnapAdmin | |
|---|---|---|
| Admin look | ✅ their whole point | Unfold's, when you install [theme] |
Who writes the ModelAdmin |
you | generated from the field kwargs |
| REST API + OpenAPI/Swagger | — | generated from the same fields |
| GraphQL schema | — | generated from the same fields |
| Elasticsearch indexing/search | — | generated from the same fields |
| Ops (audit log, GDPR purge, backups, health) | — | built in, each one setting away |
If all you want is a better-looking admin, use a theme — it is less machinery. SnapAdmin earns its place when the same models also have to be an API, a search index and an auditable system of record, and you would rather declare that once than maintain four descriptions of the same fields.
🖥 The four commands
| Command | Answers | What it does |
|---|---|---|
snapadmin-demo |
"What does this thing actually look like?" | Downloads and serves a throwaway demo project. Needs no project of your own |
snapadmin-init |
"How do I add this to my existing project?" | A read-only doctor: prints a present/missing checklist and ready-to-paste snippets. It never edits your code |
snapadmin-info |
"Is it configured correctly, and is everything up?" | Version, database, Elasticsearch, Celery, models, system checks, plus a ✓/✗ feature-adoption audit |
snapadmin-license-check |
"Can I ship this commercially?" | The licence and 🟢/🟡/🔴 tier of every installed dependency, with a verdict |
The last two inspect a live project, so run them from inside one. They are manage.py commands with
a shell shim, and every spelling works — the shim just finds your manage.py and forwards:
snapadmin-info # ≡ snapadmin_info ≡ python manage.py snapadmin_info
snapadmin-license-check # ≡ snapadmin_license_check
snapadmin-init # what's missing to wire SnapAdmin in
snapadmin-init --api --graphql # also check the REST / GraphQL config
snapadmin-info # full diagnostic report
snapadmin-info --section features # just the ✓/✗ capability checklist
snapadmin-info --health-check # probes only; non-zero exit if one fails
snapadmin-info --json # the same report for CI / monitoring
snapadmin-info --verbose # + the full text of any system-check message
snapadmin-license-check # every dependency's licence + tier
snapadmin-license-check --critical-only # only what blocks commercial use
Other management commands, all opt-in and none of them running on their own:
snapadmin_reindex (Elasticsearch), snapadmin_health_alert, snapadmin_db_backup,
snapadmin_send_error_digest, snapadmin_purge_expired_data, snapadmin_audit_export.
⏱ Nothing runs on a schedule by itself. SnapAdmin ships no daemon — the retention purge, digests and backups need a Celery Beat entry or a cron line. → Background tasks & scheduling
📦 Install
pip install django-snapadmin
Requires Python ≥ 3.10 and Django ≥ 5.2. The package is beta — the public API is
stabilising but may still change before 0.1.0 stable, so pin an exact version in production.
Minimal INSTALLED_APPS — the smallest thing that works
Everything below is installed for you by pip install django-snapadmin; you only have to list it.
INSTALLED_APPS = [
# ── Django itself ───────────────────────────────────────────────────────
"django.contrib.admin", # SnapAdmin generates ModelAdmins into this site
"django.contrib.auth", # permissions gate both the admin and the API
"django.contrib.contenttypes", # required by auth; the audit trail keys off it
"django.contrib.sessions", # admin login
"django.contrib.messages", # admin "saved successfully" banners
"django.contrib.staticfiles", # serves SnapAdmin's CSS/JS
# ── The API stack (pulled in as dependencies — just list them) ───────────
"rest_framework", # the generated REST endpoints are DRF viewsets
"drf_spectacular", # builds the OpenAPI schema behind /api/docs/
"django_filters", # backs the auto-generated ?field=… query filters
"graphene_django", # the generated GraphQL schema
# ── SnapAdmin ───────────────────────────────────────────────────────────
"snapadmin",
"myapp", # …your own apps
]
# urls.py
from django.contrib import admin
from django.urls import include, path
urlpatterns = [
path("admin/", admin.site.urls),
path("api/", include("snapadmin.urls")), # REST + Swagger + GraphQL
]
That's a working install: themed-less admin, REST, Swagger and GraphQL. Turning a surface off with
SNAPADMIN_*_ENABLED = False removes its routes (404) but you still list the app.
Full INSTALLED_APPS — everything switched on
Each block below corresponds to one optional extra. Add the block and the extra, or neither.
INSTALLED_APPS = [
# ── Themed UI — pip install django-snapadmin[theme] ─────────────────────
# MUST come before django.contrib.admin: Unfold overrides admin templates,
# and Django resolves templates in INSTALLED_APPS order.
"unfold",
"unfold.contrib.filters", # the sidebar range/dropdown filters SnapAdmin generates
"unfold.contrib.forms", # themed form widgets
"unfold.contrib.inlines", # themed inline formsets
# ── Rich text — pip install django-snapadmin[wysiwyg] ───────────────────
# Only needed for wysiwyg=True / SnapRichTextField. Bundles CKEditor 5,
# which is GPL-or-commercial — that is why it is not a core dependency.
"django_ckeditor_5",
# ── Django itself ───────────────────────────────────────────────────────
"django.contrib.admin",
"django.contrib.auth",
"django.contrib.contenttypes",
"django.contrib.sessions",
"django.contrib.messages",
"django.contrib.staticfiles",
# ── The API stack (always required) ─────────────────────────────────────
"rest_framework",
"drf_spectacular",
"django_filters",
"graphene_django",
# ── SnapAdmin ───────────────────────────────────────────────────────────
"snapadmin",
# ── Background tasks — pip install django-snapadmin[celery] ─────────────
# Needed for the GDPR purge, async exports, error digests and backups.
"django_celery_beat", # edit the schedule from the admin
"django_celery_results", # store task results in the database
# ── Admin-editable settings — pip install django-snapadmin[extra-settings]
# SnapAdmin does not use it; add it if you want a runtime key/value Setting
# model in the admin (the demo shows the pattern).
"extra_settings",
# ── Autocomplete list filters — [autocomplete-filter] (LGPL) ────────────
# For your own AutocompleteFilter admin filters; SnapAdmin core never imports it.
"admin_auto_filters",
"myapp",
]
Elasticsearch needs no app entry —
pip install django-snapadmin[elasticsearch]and setELASTICSEARCH_ENABLED = True. Same for[backup](SFTP offsite backups): a dependency, not an app.
Optional extras
The base install is self-contained and carries only permissive licences (MIT/BSD/Apache), so it is safe for commercial and proprietary use. Everything with a licence caveat is opt-in:
| Extra | Pulls in | Gives you |
|---|---|---|
theme |
django-unfold |
The themed admin UI (stock Django admin without it) |
elasticsearch |
elasticsearch |
Full-text search, DUAL / ES_ONLY models |
celery |
celery, django-celery-beat, django-celery-results |
Background tasks: async export, GDPR purge, digests, backups |
backup |
paramiko |
SFTP offsite database backups |
extra-settings |
django-extra-settings |
An in-admin dynamic key/value Setting model |
wysiwyg |
django-ckeditor-5 |
Rich-text fields — bundles CKEditor 5 (GPL-or-commercial) |
autocomplete-filter |
django-admin-autocomplete-filter |
AutocompleteFilter list filters (LGPL) |
all |
everything above | — |
Run python manage.py snapadmin_license_check after installing to see exactly what you ended up
with and whether it is still proprietary-safe.
→ Full installation guide — compatibility matrix, extras gotchas, and the MySQL driver licence note.
Adding it to a project you already have
pip install django-snapadmin
snapadmin-init
snapadmin-init inspects your project and prints a per-item checklist plus the exact snippets to
paste — the INSTALLED_APPS ordering, the URL include, the settings block. It edits nothing, so
there is no bad automatic change to undo.
→ Integration guide
✨ What you get
Admin
list_display,search_fieldsandlist_filterderived from the field kwargs — noModelAdminto write- Themed responsive UI with the
[theme]extra, colour-coded status badges, tabs and horizontal field rows - Date and numeric range filters, autocomplete foreign keys, inlines
- Field-level change logging (
old → new) with a history view - Offline mode — a per-model IndexedDB cache that keeps a list view usable with no connection and syncs on reconnect
APIs
- REST CRUD per model, with Swagger + ReDoc and filters derived from each field's type — ranges,
__in,__isnull, text lookups, JSON paths - GraphQL from the same models, with permissions enforced on every traversed relation
- API tokens hashed at rest, shown once, scoped per model — or plug in JWT / session / your own auth
- Per-model guards:
api_exclude_fields(never leaves the server),api_write_fields(mass-assignment allowlist),api_read_only(writes answer 405), PII masking
Search (optional)
- Per-model
DB_ONLY/DUAL/ES_ONLYstorage, with the index mapping derived from the fields ?search=on aDUALmodel is routed to Elasticsearch automatically (fuzzy, ranked); plain listings stay on SQL- Query helpers that fall back to the database when ES is down:
es_filter(),es_aggregate(),es_count(),es_scan() - Resumable bulk reindex with live progress,
--resume,--paralleland--limit
Operations
- GDPR retention (
data_retention_days) and an immutable audit trail - Error monitoring with spike alerts and daily grouped digests, plus health-probe emails when a subsystem goes down
- 3-2-1 database backups — local, network share, offsite FTPS/SFTP
- Large-dataset tuning — automatic
list_select_related(no admin N+1), estimated counts, paging caps - ETL helpers —
upsert_from_source()andstale_sync()with a wipe guard - Structured logging via
structlog; the UI is translated into 10 locales
How fast is it?
There are no published benchmark numbers, so this README will not quote any. What ships instead is the means to measure it on your own hardware and data shape, which is the only figure worth acting on:
python demo/manage.py seed_large # 100,000 customers and orders, batched bulk_create
python demo/manage.py benchmark_list_view # admin changelist: query count + wall time
benchmark_list_view runs the changelist queryset with and without SnapAdmin's automatic
list_select_related, touching a foreign key on every row, so the N+1 it removes shows up in the
query count rather than in prose. What is designed in — rather than measured — is documented under
large-dataset tuning: estimated counts in
place of COUNT(*) on large tables, paging caps, and streaming exports and es_scan() that hold
memory flat regardless of result size.
⚙️ Configuration
Every surface is a plain Django setting; switching one off removes its URL routes entirely (404):
SNAPADMIN_REST_API_ENABLED = True # REST CRUD endpoints
SNAPADMIN_GRAPHQL_ENABLED = True # GraphQL endpoint
SNAPADMIN_SWAGGER_ENABLED = True # Swagger UI + ReDoc
SNAPADMIN_ES_QUERY_ROUTING = True # route ?search= on DUAL models to Elasticsearch
SNAPADMIN_GRAPHQL_REQUIRE_AUTH = True # auth + per-model perms on every resolver
SNAPADMIN_URL_PREFIX = "" # relocate the whole API surface
Misconfiguration shows up at startup as a Django system check (snapadmin.W001–W007), not as a
mystery at request time — read those first when something behaves unexpectedly.
→ Full settings reference — every
SNAPADMIN_* knob with its default, grouped by area.
🧩 Extending
SnapAdmin is meant to be customised, not forked:
- Add field types — subclass
SnapFieldwith your own admin introspection - Extend a
SnapModel— overridesave(), add managers, mix in your own behaviour - Add or override REST endpoints — mount your router before SnapAdmin's
- Swap auth, permissions and the ES client — configuration, no code
- Override admin templates and the dashboard — standard Django template resolution
→ Extending & Overriding guide
🌟 The demo, the long way
snapadmin-demo (above) is the fast path. From a clone you also get the full Docker stack with
PostgreSQL, Redis and Elasticsearch. The demo lives under
demo/ with example models (Product,
Customer, Order) and a seeded database; it is not published to PyPI — only snapadmin/ is.
git clone https://github.com/drofji/django-snapadmin.git
cd django-snapadmin
cp demo/dist.env demo/.env
docker compose -f demo/docker-compose.yml up --build
Then open http://localhost:8000/admin/ (admin / admin).
→ Demo guide — Traefik overlays with HTTPS, the Elasticsearch profile, manual setup without Docker, and the seed command.
📖 Documentation
| Topic | |
|---|---|
| Getting started | Installation · Integrate an existing project · SnapModel · Field types · Admin registration |
| APIs | REST · GraphQL · Tokens · Integrating auth / JWT / ETL |
| Search | Elasticsearch modes · Query routing · Filters · Facets · Deep scan |
| Operations | Diagnostics (snapadmin_info) · Licence audit · Celery & scheduling · GDPR · Backups · Error monitoring · Performance |
| Reference | All settings · Theming · Enterprise config · Extending · Migration guides |
Upgrading from drofji-automatically-django-admin? See the
migration guide.
🔒 Security
API tokens are hashed at rest, rich-text HTML is sanitized before display, GraphQL enforces permissions on every traversed relation, and PII masking is available on both APIs. Report vulnerabilities privately — see SECURITY.md for the policy, the supported-versions row, and the production-hardening checklist.
Third-party dependency licences are inventoried in
THIRD_PARTY_NOTICES.md
— or run python manage.py snapadmin_license_check for the same inventory computed from what you
actually installed, with a commercial-usability verdict.
🤝 Contributing
See CONTRIBUTING.md. The
suite must stay green with 100% coverage on snapadmin/:
pytest
📜 License
MIT — see LICENSE.
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 django_snapadmin-0.1.0b6.tar.gz.
File metadata
- Download URL: django_snapadmin-0.1.0b6.tar.gz
- Upload date:
- Size: 529.4 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
d21af10dca18edc93b9b2d50cf62c3c958d45891102ae76b50f6f2346bcb9c7d
|
|
| MD5 |
50fe241f6f8f41dbf8b168f1295c2e73
|
|
| BLAKE2b-256 |
952154d2bdde2218ce4432193861c9422e5a324b164d2eb7fb51172d217b777c
|
Provenance
The following attestation bundles were made for django_snapadmin-0.1.0b6.tar.gz:
Publisher:
publish.yml on drofji/django-snapadmin
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
django_snapadmin-0.1.0b6.tar.gz -
Subject digest:
d21af10dca18edc93b9b2d50cf62c3c958d45891102ae76b50f6f2346bcb9c7d - Sigstore transparency entry: 2441508304
- Sigstore integration time:
-
Permalink:
drofji/django-snapadmin@a4e6356d5f7d2c65968e00f653d1bcddc8317787 -
Branch / Tag:
refs/tags/v0.1.0b6 - Owner: https://github.com/drofji
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@a4e6356d5f7d2c65968e00f653d1bcddc8317787 -
Trigger Event:
push
-
Statement type:
File details
Details for the file django_snapadmin-0.1.0b6-py3-none-any.whl.
File metadata
- Download URL: django_snapadmin-0.1.0b6-py3-none-any.whl
- Upload date:
- Size: 571.8 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
b992301b67beb473684ec09818e5baaf2b4318ee9cde938bb2a2de88a2cb2b5a
|
|
| MD5 |
88fa1faf9ac9541659eedc71b9926a0c
|
|
| BLAKE2b-256 |
1bc7a7457d8bbd67201a7aad4ade0aa1f2b4e9a7a39faf28feeefe3f43cbf24f
|
Provenance
The following attestation bundles were made for django_snapadmin-0.1.0b6-py3-none-any.whl:
Publisher:
publish.yml on drofji/django-snapadmin
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
django_snapadmin-0.1.0b6-py3-none-any.whl -
Subject digest:
b992301b67beb473684ec09818e5baaf2b4318ee9cde938bb2a2de88a2cb2b5a - Sigstore transparency entry: 2441508390
- Sigstore integration time:
-
Permalink:
drofji/django-snapadmin@a4e6356d5f7d2c65968e00f653d1bcddc8317787 -
Branch / Tag:
refs/tags/v0.1.0b6 - Owner: https://github.com/drofji
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@a4e6356d5f7d2c65968e00f653d1bcddc8317787 -
Trigger Event:
push
-
Statement type: