This release is a pre-release and may not be stable for production use.
Alpha (
0.1.0a1). Working and heavily tested — more than 3,600 tests — but the API may still move. See the FAQ for an honest account of what is and is not done. Use it on an internal tool, and pin the exact version if you depend on it.
Why
Django's built-in admin is remarkable for what it is, and it hasn't fundamentally changed in
twenty years. django-vantage keeps what made it great — configuration in ordinary Python, next
to your models — and rebuilds everything else: the rendering, the data grid, the relationship
handling, and the look.
Two rules govern the whole package:
- Smart. Install it, and every model in your project already has a good admin — columns, filters, search, widgets, and query optimisation inferred automatically.
- Easy. Every inferred default is overridable in one line. If it takes more, the API is wrong.
Install
pip install --pre django-vantage
--pre is needed while Vantage is in alpha, because pip skips pre-releases unless asked.
# settings.py
INSTALLED_APPS = ["vantage", ...]
# urls.py
from django.urls import path
from vantage import site
urlpatterns = [path("admin/", site.urls)]
That is the entire setup. Every model is now administrable, with a dashboard, a searchable data grid, create/edit/delete, a command palette, light and dark themes, and a schema map.
Customise a model
Create panel.py next to your models.py:
from vantage import ModelPanel, register
from blog.models import Post
@register(Post)
class PostPanel(ModelPanel):
list_display = ["title", "author", "status", "published_at"]
Overriding list_display overrides only the columns. Filters, search fields and widgets stay
inferred. Partial configuration never opts you out of the intelligence.
Not sure what it inferred? Ask it to write it down:
python manage.py vantage_scaffold blog
That prints a real panel.py containing exactly what Vantage decided, which you can then own
and edit. Delete an attribute and it goes back to being inferred.
Statistics and charts on any list page
Declared in Python, computed against the currently filtered queryset, no JavaScript:
from vantage import Chart, ModelPanel, Stat, register
from django.db.models import Avg, Count, Q, Sum
@register(Order)
class OrderPanel(ModelPanel):
list_filter = ["status", "created_at"]
list_stats = [
Stat("Revenue", Sum("total"), format="currency", trend="created_at"),
Stat("Orders", Count("id")),
Stat("Avg order", Avg("total"), format="currency"),
Stat("Refunded", Count("id", filter=Q(status="refunded")), tone="destructive"),
]
list_charts = [
Chart.timeseries("Revenue over time", date="created_at", value=Sum("total")),
Chart.breakdown("By status", group_by="status", value=Count("id"), kind="donut"),
]
Change a filter and every number follows. Filter to the last 90 days and Revenue compares
itself with the 90 days before. All four stats fold into one shared aggregate() call, plus
one more for the trend window — two queries total, however many non-trending stats you add.
The charts render as inline SVG that follows your brand colour and the current theme.
There is also a per-column summary bar at the foot of every table, which needs no configuration at all — a money column is summed, a rating column is averaged, and an identifier is left alone.
|
Dashboard — widgets declared in Python or arranged from the UI |
Command palette — ⌘ K to go anywhere |
|
Forms — inferred widgets and sections, or your own layout |
Schema map — derived from the registry, never out of date |
Bulk actions
Ordinary methods with a decorator:
from vantage import ModelPanel, action, register
@register(Review)
class ReviewPanel(ModelPanel):
@action("Publish", icon="check")
def publish(self, request, queryset):
return f"Published {queryset.update(is_published=True)} reviews."
@action("Delete", destructive=True, permission="delete", max_rows=500,
confirm="Permanently delete {count} reviews?")
def delete_selected(self, request, queryset):
deleted, _ = queryset.delete()
return f"Deleted {deleted} records."
"Select all matching" travels as a predicate plus an exclusion set, never as a list of ids, so selecting forty thousand rows costs the same as selecting three. The scope actually resolved is always reported back — a bulk action must never quietly apply to less than you asked for.
Lay out a form
from vantage import Row, Section, Tab, Tabs
layout = Tabs(
Tab("Content", Section(Row("title", "slug"), "body")),
Tab("SEO", Section("meta_title", "meta_description")),
)
A field the layout forgets is appended rather than dropped, and a field it names that does not
exist fails manage.py check rather than silently vanishing from the form.
Brand it
VANTAGE = {
"SITE_TITLE": "Acme",
"LOGO": "img/acme.svg",
"BRAND_COLOR": "#0ea5e9",
"THEME": "system",
}
One colour produces a complete palette for both themes, with contrast checked: the derived primary and its foreground are guaranteed to clear WCAG AA for any hue you supply.
Anything here can also be edited in the panel by a superuser — logo upload, brand colour, title, theme — and the database value overrides the file.
Organise the sidebar
VANTAGE = {
"NAV": [
{"route": "vantage:dashboard", "label": "Overview", "icon": "layout-dashboard"},
{"group": "Commerce", "items": [
{"model": "shop.Order", "badge": "live"},
{"model": "shop.Product", "label": "Catalogue"},
]},
{"group": "Elsewhere", "items": [
{"url": "https://status.example.com", "label": "Status"},
]},
],
}
Declaring a nav can reorder, rename and hide — it can never grant access. Every entry still passes through the permission layer.
Add your own page
from vantage import Page, site
@site.register_page
class RevenuePage(Page):
title = "Revenue"
icon = "trending-up"
def get_context(self, request):
return {"total": Order.objects.total_revenue()}
A class, a title, and a method. The URL, the nav entry and the rendering all have defaults, so a page with no template of its own still produces a presentable screen.
Configure it from the UI too
Everything above is the developer's API. The person running the panel day to day can
often reach the same knobs without a deploy: a list's columns, filters, stats and
charts have a customisation rail, the dashboard has its own "Add a card" editor, and
branding and behaviour have a settings screen. All of it composes in one fixed
order — UI override → panel.py / VANTAGE → inference — stated once in
How configuration resolves,
so "why is my panel.py being ignored?" has a single, findable answer.
What else is in the box
| JSON API | Every panel, through the same permission and validation path as the UI — cursor pagination, optimistic concurrency, errors as structured data. Docs → |
| Command palette | ⌘ K with global search across every model you can see |
| Dashboard | Widgets declared in Python, or built and arranged from the UI — stat, chart, table, list, or your own template |
| Soft delete | A real Trash tab and Restore on any model with a deleted_at or is_deleted column, detected automatically |
| Duplicate and bulk edit | Both reuse the exact form and validation a single record uses |
| Multi-tenancy | Scope every registered model to a tenant with one setting, fail-closed. Docs → |
| Cascade-delete preview | The real tree of what a delete takes with it, counted |
| Read-only mode, impersonation | Both enforced in the permission layer |
| System health page | Migrations, cache, database, task backend |
| Streaming CSV export, CSV import | Export honours the filters and sort you are looking at; import maps, previews and validates first |
| Light, dark and system themes, RTL | All from day one, with en and ar catalogues shipped |
| Query-count guarantees | select_related and prefetch_related are derived from what the page renders, and tests assert the cost does not grow with the row count |
Requirements
Python 3.12, 3.13, 3.14 · Django 5.2 LTS, 6.0, 6.1
Django is the only runtime dependency. No Node.js — assets ship prebuilt. django-guardian, django-rules and django-simple-history are used if present, never required.
Try the demo
A full demo shop lives in example/:
git clone https://github.com/Pythondeveloper6/django-vantage
cd django-vantage
uv sync --all-groups
./example/run.sh # migrate, seed, and serve on :8765
Sign in as admin / admin12345.
Contributing
Contributions are welcome — bug reports, docs fixes and pull requests alike. Start with CONTRIBUTING.md, and please report security issues privately as described in SECURITY.md.
uv sync --all-groups
uv run pytest
uv run ruff check src tests
uv run mkdocs serve # the documentation site, on :8000
Licence
django-vantage is open source under the MIT licence. Copyright © 2026 Mahmoud Ahmed.
You may use, modify and redistribute it, commercially or not, as long as the copyright notice and the licence text are kept in every copy or substantial portion of the code. If django-vantage helps your project, a link back to this repository is always appreciated.
Metadata
Release files for django-vantage 0.1.0a1
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 | |
|---|---|---|---|---|
| django_vantage-0.1.0a1.tar.gz | 1.1 MB | With provenance | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| django_vantage-0.1.0a1-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 1.9 MB
Release files / django_vantage-0.1.0a1.tar.gz
| Download URL | django_vantage-0.1.0a1.tar.gz |
|---|---|
| Size | 1.1 MB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
30252aade27b506e5b9c56b5a80722d2b36058d4ab87d70b821bc4c51bd0efaf
|
|
BLAKE2b-256 checksum How to use checksums |
933695d344b996d42f96487338188ac6fc57b2e608caa82a0f6e73bc71262c86
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
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.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 25, 2026.
Transparency logRelease files / django_vantage-0.1.0a1-py3-none-any.whl
| Download URL | django_vantage-0.1.0a1-py3-none-any.whl |
|---|---|
| Size | 794.8 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
4214b1deb4f9c59fa42e0a0db7ec11db0be89b5af761e21540df02566eb63267
|
|
BLAKE2b-256 checksum How to use checksums |
5fe6ea731eb6c737dd6d82117d96cbfe123905ac1e5901d7dc2b8417cbaf2efb
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
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.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 25, 2026.
Transparency logSecurity for django-vantage 0.1.0a1
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.
Full provenance
All 2 files in this release have been uploaded with provenance metadata.
Attestation for django_vantage-0.1.0a1.tar.gz
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 25, 2026.
Transparency logA 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.