Skip to content

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:

  1. Action-Centric Design: Everything is an action. Views are generated from actions, not the other way around.
  2. Plugin-First Extensibility: Core functionality is implemented as plugins, allowing deep customization without forking.
  3. 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