>Is It Time for a Django Admin Rewrite?
Django-Admin-Deux: a ground-up admin rewrite using generic class-based views, a plugin system, and factory-generated views.
Emma Delescolle’s talk at DjangoCon Europe 2026 on Django-Admin-Deux, a ground-up admin rewrite.
Why
Django’s admin is 20 years old. It’s a framework inside a framework — its own patterns, its own view system, its own template hierarchy. Third-party packages collide because there’s no plugin architecture. Class-based views, dataclasses, type hints, Django’s generic views — none of these existed when the admin was written. It doesn’t use them.
How Admin-Deux works
Factory-generated views. At startup, a view factory dynamically creates view classes using type(), composing base views + plugin mixins. The result is standard Django class-based views.
Actions as recipes. Every operation (list, create, update, delete) is an “Action” — a dataclass describing what view to generate. Custom actions (export PDF, send notification) work the same way as CRUD.
Plugin-first design. Built on djp (which uses pluggy from pytest). Plugins auto-register on pip install. Even core CRUD is implemented as a plugin.
Side-by-side migration. Runs on a separate URL (/djadmin/) alongside stock admin. Migrate one model at a time.
Using it
Registration is near-identical to stock admin. Create djadmin.py in your app:
from djadmin import ModelAdmin, register, Column
from djadmin.dataclasses import Filter
@register(Product)
class ProductAdmin(ModelAdmin):
list_display = [
"name",
Column("sku", label="SKU Code", classes="font-mono"),
Column("category", filter=True, order=True),
Column("price", order=True, filter=Filter(lookup_expr=["gte", "lte"])),
]
Mix plain strings and Column objects. filter=True gives exact-match filtering; Filter(...) for range lookups.
Form layouts with Layout, Fieldset, Row, and Field:
from djadmin import Layout, Field, Fieldset, Row
@register(Author)
class AuthorAdmin(ModelAdmin):
layout = Layout(
Fieldset("Personal Information",
Row(
Field("first_name", css_classes=["flex-1", "pr-2"]),
Field("last_name", css_classes=["flex-1", "pl-2"]),
),
Field("birth_date", label="Date of Birth"),
),
Fieldset("Biography",
Field("bio", widget="textarea", attrs={"rows": 8}),
),
)
The plugin system
Plugins use djp/pluggy hooks. Auto-register on install when using djadmin_apps():
from djadmin.plugins import hookimpl
@hookimpl
def djadmin_provides_features():
return ["search", "filter"]
@hookimpl
def djadmin_get_action_view_mixins(action):
from djadmin.plugins.core.actions import ListAction
from .mixins import SearchMixin
return {ListAction: [SearchMixin]}
Available plugins: core (CRUD, search), djadmin-filters (filtering, ordering), djadmin-formset (form rendering, inlines), djadmin-classy-doc (auto-generated view docs), djadmin-rest2 (REST API views).
Key takeaways
- Admin-deux uses Django’s own tools — generic views, forms, querysets. If you know Django, you know how to extend it
- Plugin-first means no more collisions between third-party packages
- Migration is gradual — run both admins side by side
- This is alpha software (0.1.6) but the architecture is solid and ready for feedback
- Dark mode is built in