Architecture Overview¶
This document provides a high-level overview of django-admin-deux's architecture, explaining how the major components fit together and interact.
Core Philosophy¶
django-admin-deux is built on three foundational principles:
- Action-Centric Design: Everything is an action. Views are generated from actions, not the other way around.
- Plugin-First Extensibility: Core functionality is implemented as plugins, allowing deep customization without forking.
- Composition Over Inheritance: Components are assembled through mixins and factories rather than deep inheritance chains.
System Architecture¶
┌─────────────────────────────────────────────────────────────┐
│ Django Project │
├─────────────────────────────────────────────────────────────┤
│ urls.py: path('admin/', include(admin_site.urls)) │
└────────────────────┬────────────────────────────────────────┘
│
┌────────────────────▼────────────────────────────────────────┐
│ AdminSite │
├─────────────────────────────────────────────────────────────┤
│ • Model registry (model -> ModelAdmin) │
│ • URL pattern generation │
│ • Plugin manager access │
└────────────────────┬────────────────────────────────────────┘
│
┌────────────┴────────────┐
│ │
┌───────▼────────┐ ┌───────▼────────┐
│ ModelAdmin │ │ Plugin System │
├────────────────┤ ├────────────────┤
│ • list_display │ │ • djp/pluggy │
│ • Actions │ │ • Hook specs │
│ • Features │ │ • Discovery │
└───────┬────────┘ └───────┬────────┘
│ │
│ ┌──────────────┘
│ │
┌───────▼─────────▼────────────────────────────────────────────┐
│ Action System │
├───────────────────────────────────────────────────────────────┤
│ BaseAction (model, model_admin, admin_site) │
│ ├─ GeneralActionMixin → ListView entry point │
│ ├─ BulkActionMixin → Operate on selected records │
│ └─ RecordActionMixin → Operate on single record │
│ │
│ View-Type Mixins (specify Django CBV type): │
│ ├─ ListActionMixin → ListView │
│ ├─ CreateViewActionMixin → CreateView │
│ ├─ UpdateViewActionMixin → UpdateView │
│ └─ FormViewActionMixin → FormView │
└───────────────────────┬───────────────────────────────────────┘
│
┌───────────────────────▼───────────────────────────────────────┐
│ ViewFactory │
├───────────────────────────────────────────────────────────────┤
│ 1. Get base class (ListView, CreateView, etc.) │
│ 2. Collect mixins from plugins │
│ 3. Build class attributes │
│ 4. Generate view class: type(name, bases, attrs) │
└───────────────────────┬───────────────────────────────────────┘
│
┌───────────────────────▼───────────────────────────────────────┐
│ Generated Django CBV │
├───────────────────────────────────────────────────────────────┤
│ ProductListView(Mixin1, Mixin2, ListView): │
│ action = <action_instance> │
│ model = Product │
│ model_admin = ProductAdmin │
│ paginate_by = 100 │
│ get_queryset() = bound from action │
└───────────────────────────────────────────────────────────────┘
Request Flow¶
Here's how a typical request flows through the system:
1. List View Request¶
HTTP GET /admin/products/
1. Django URL routing → admin_site.urls
2. AdminSite matches pattern → model_admin for Product
3. ModelAdmin provides general_actions (includes ListAction)
4. ListAction generates view via ViewFactory:
┌─────────────────────────────────────────────┐
│ ViewFactory.create_view(action) │
│ ├─ Get base: ListView (from mixin) │
│ ├─ Get mixins from plugins (pagination, │
│ │ breadcrumbs, context, etc.) │
│ ├─ Build attrs: model, paginate_by, │
│ │ get_queryset(), etc. │
│ └─ Return: type('ProductListView', ...) │
└─────────────────────────────────────────────┘
5. Generated view.as_view() handles request
6. Plugins modify queryset (permissions, filters)
7. Plugins add context data (breadcrumbs, actions)
8. Template renders with context
2. Create View Request (Add Action)¶
HTTP GET /admin/products/add/
1. URL routing → AddAction (from general_actions)
2. AddAction is a GeneralActionMixin + CreateViewActionMixin
3. ViewFactory generates CreateView:
┌─────────────────────────────────────────────┐
│ ViewFactory.create_view(add_action) │
│ ├─ Base: CreateView (from mixin) │
│ ├─ Mixins: FormMixin, SuccessMessageMixin │
│ ├─ Attrs: form_class, fields, success_url │
│ └─ Return: type('ProductAddActionView', ...)│
└─────────────────────────────────────────────┘
4. View handles GET/POST with standard Django CBV flow
5. Template renders form
3. Record Action Request (Edit)¶
HTTP GET /admin/products/123/edit/
1. URL routing → EditAction (from record_actions)
2. EditAction is RecordActionMixin + UpdateViewActionMixin
3. ViewFactory generates UpdateView with pk=123
4. View retrieves object, renders update form
5. POST follows standard Django form processing
Key Components¶
AdminSite¶
Location: djadmin/sites.py
The central registry that: - Stores model → ModelAdmin mappings - Generates URL patterns for all registered models - Provides access to the plugin manager - Creates dashboard views
# Registration
admin_site.register(Product, ProductAdmin)
# URL generation
urlpatterns = [path('admin/', admin_site.urls)]
ModelAdmin¶
Location: djadmin/options.py
Configuration class for each model that: - Declares display options (list_display, pagination) - Declares action lists (general, bulk, record) - Declares feature requests (search, filter, ordering) - Provides model metadata access
class ProductAdmin(ModelAdmin):
list_display = ['name', 'price', 'stock']
general_actions = [ListAction, AddAction]
record_actions = [EditAction, DeleteAction]
Action System¶
Location: djadmin/actions/
Actions represent operations on models. Every action: - Carries context (model, model_admin, admin_site) - Declares display properties (label, icon) - Specifies view generation through mixins - Implements business logic
See action-system.md for deep dive.
ViewFactory¶
Location: djadmin/factories/base.py
Generates Django CBV classes from actions using: - Base class from action's view-type mixin - Mixins from plugins (based on action type) - Attributes from action and plugins - Standard Django type() constructor
See view-factory.md for internals.
Plugin System¶
Location: djadmin/plugins/
Built on djp (Django plugin system using pluggy):
- Auto-discovers plugins from djadmin_hooks.py modules
- Defines hook specifications for extension points
- Core and theme plugins provide base functionality
- Third-party plugins add features (search, export, etc.)
See plugin-system.md for details.
Data Flow Diagrams¶
Model Registration Flow¶
Developer Code AdminSite ModelAdmin
───────────────── ─────────── ──────────
site.register(Product) ─────────▶ Create ModelAdmin ────────▶ Initialize
ProductAdmin Store mapping ├─ Normalize list_display
Register with plugins ├─ Load plugin defaults
└─ Instantiate actions
View Generation Flow¶
URL Request AdminSite Action ViewFactory Plugin
─────────── ────────── ────── ─────────── ──────
GET /products/ ────▶ Lookup model ────▶ Get action ────▶ Create view ◀──── Provide mixins
Get ModelAdmin (ListAction) ├─ Base class Provide attrs
Resolve action ├─ Mixins list Provide assets
├─ Attributes
└─ Generate class
│
◀───────────────────────────┘
Django CBV ready for .as_view()
Extension Points¶
The system provides extension points at multiple levels:
1. ModelAdmin Level (Configuration)¶
class ProductAdmin(ModelAdmin):
# Override display
list_display = [Column('name'), Column('price', label='Unit Price')]
# Override actions
general_actions = [CustomListAction, AddAction]
# Request features
search_fields = ['name', 'description']
2. Action Level (Behavior)¶
class CustomExportAction(DownloadActionMixin, BulkActionMixin, BaseAction):
label = "Export to Excel"
icon = "download"
def get_download_response(self, request, queryset):
# Custom export logic
return excel_response
3. Plugin Level (System-Wide)¶
@hookimpl
def djadmin_get_action_view_mixins(action):
from djadmin.actions.list_view import ListAction
return {
ListAction: [SearchMixin, FilterMixin]
}
4. Template Level (Presentation)¶
templates/djadmin/
├─ webshop/product_list.html # Model-specific
├─ {app}/{model}_list.html or actions/list.html # Generic fallback
└─ includes/
└─ actions_toolbar.html # Shared partial
Comparison with Django Admin¶
| Aspect | Django Admin | django-admin-deux |
|---|---|---|
| Architecture | View-centric (ModelAdmin generates views) | Action-centric (actions generate views) |
| Extensibility | Subclass ModelAdmin, override methods | Plugin system + action composition |
| Customization | Deep method overrides, template blocks | Mixins, plugins, clean separation |
| Actions | Functions with signatures | First-class objects with context |
| Views | Hardcoded CRUD views | Factory-generated from actions |
| URLs | Fixed patterns | Action-defined patterns |
| Plugin System | None (app-level only) | djp with hook specifications |
Design Trade-offs¶
See design-decisions.md for detailed rationale on: - Why action-centric over view-centric - Why djp over raw pluggy or other systems - Why factory pattern over inheritance - Why semantic HTML over heavy JavaScript - Performance implications and optimizations