Open-Source Wikis

/

Django

/

Django

/

Architecture

django/django

Architecture

Django is organised around the request/response cycle. A request enters through a server gateway (WSGI or ASGI), gets dispatched through middleware, routed to a view, and the view returns a response that travels back through the same middleware stack. Most of the framework's other subsystems — the ORM, templates, forms, sessions, auth, the admin — exist to make views easy to write.

High-level component map

graph TD
    Server[WSGI/ASGI server<br/>gunicorn, uvicorn, daphne]
    Handler["django.core.handlers<br/>WSGIHandler / ASGIHandler"]
    MW[Middleware chain]
    Resolver["django.urls<br/>URLResolver"]
    View["django.views<br/>View / function view"]
    ORM["django.db.models<br/>QuerySet, Manager"]
    DB[(Database backend)]
    Tpl["django.template<br/>Engine, Loader"]
    Forms["django.forms"]
    Resp["django.http<br/>HttpResponse"]

    Server -->|environ / scope| Handler
    Handler --> MW
    MW --> Resolver
    Resolver --> View
    View --> ORM
    ORM --> DB
    View --> Tpl
    View --> Forms
    View --> Resp
    Resp --> MW
    MW --> Handler
    Handler -->|response| Server

Each box maps to a specific package in the source tree. The handler glue lives in django/core/handlers/ (base.py, wsgi.py, asgi.py, exception.py). Middleware classes live in django/middleware/ and django/contrib/*/middleware.py. Routing lives in django/urls/ (resolvers.py is the workhorse). The view layer is split between functional views and class-based views in django/views/generic/.

Request lifecycle

sequenceDiagram
    participant Client
    participant Server as WSGI/ASGI server
    participant Handler as BaseHandler
    participant MW as Middleware
    participant Resolver as URLResolver
    participant View
    participant DB

    Client->>Server: HTTP request
    Server->>Handler: environ / scope
    Handler->>Handler: HttpRequest construction
    Handler->>MW: process_request (each MW)
    MW->>Resolver: resolve(path)
    Resolver-->>MW: ResolverMatch (view, args, kwargs)
    MW->>View: view(request, *args, **kwargs)
    View->>DB: ORM queries (optional)
    DB-->>View: rows
    View-->>MW: HttpResponse
    MW->>Handler: process_response (reverse order)
    Handler-->>Server: status, headers, body
    Server-->>Client: HTTP response

BaseHandler.get_response() in django/core/handlers/base.py is the orchestrator. It compiles the middleware chain once at startup (load_middleware()), constructs the inner _get_response callable, and dispatches the request. The async path in ASGIHandler (django/core/handlers/asgi.py) mirrors the sync path with async def versions and the asgiref.sync adapters wrapping any sync-only middleware.

Subsystem topology

graph LR
    subgraph "django.conf"
        Settings[settings lazy object]
    end

    subgraph "django.apps"
        AppRegistry[AppRegistry]
    end

    subgraph "Request layer"
        HTTP[django.http]
        URLs[django.urls]
        Views[django.views]
        MWares[django.middleware]
    end

    subgraph "Data layer"
        ORM[django.db.models]
        Backends[django.db.backends]
        Migrations[django.db.migrations]
    end

    subgraph "Presentation"
        Templates[django.template]
        Forms[django.forms]
    end

    subgraph "django.core"
        Handlers[handlers]
        Mgmt[management]
        Cache[cache]
        Mail[mail]
        Signing[signing]
        Validators[validators]
    end

    subgraph "django.contrib"
        Admin[admin]
        Auth[auth]
        ContentTypes[contenttypes]
        Sessions[sessions]
        Messages[messages]
        Sites[sites]
        StaticFiles[staticfiles]
        GIS[gis]
        Postgres[postgres]
    end

    Settings --> AppRegistry
    AppRegistry --> ORM
    AppRegistry --> Admin
    Handlers --> MWares
    MWares --> URLs
    URLs --> Views
    Views --> Templates
    Views --> Forms
    Views --> ORM
    ORM --> Backends
    ORM --> Migrations
    Forms --> ORM
    Admin --> ORM
    Admin --> Forms
    Admin --> Templates
    Auth --> ORM
    Auth --> Sessions

Key cross-cutting systems

The apps registrydjango/apps/registry.py exposes a singleton AppRegistry that tracks every installed app, its models, and the order they were loaded. django.setup() (in django/__init__.py) populates it. Almost every other subsystem reaches into the registry: the ORM uses it to resolve string references like "auth.User", the admin uses it to autodiscover admin.py modules, migrations use it to find models per app label.

Settingsdjango/conf/__init__.py defines a lazy settings object that imports the user's settings module on first access. global_settings.py provides the defaults. Almost every subsystem reads from settings, so changing a setting at runtime usually does the wrong thing.

Signalsdjango/dispatch/dispatcher.py provides a synchronous publish/subscribe primitive. Built-in signals include pre_save/post_save, pre_delete/post_delete, request_started/request_finished, setting_changed, and the auth signals. Signals are used internally to decouple the ORM from contrib apps that need to react to changes (e.g., django.contrib.contenttypes listens to post_migrate).

System checksdjango/core/checks/ is a registration framework that runs a set of validation functions at startup and as part of manage.py check. Each subsystem that needs validation registers tags (models, admin, urls, templates, security, etc.). Checks run in the same process and produce Warning/Error messages with stable IDs.

Async/sync boundary — Django supports both sync and async views and middleware. The dual support is implemented with asgiref.sync.async_to_sync and sync_to_async wrappers. django/utils/asyncio.py adds the async_unsafe decorator for code paths that must run outside an async event loop. Most middleware can be marked sync_capable and async_capable simultaneously and the handler chooses the cheaper path.

How data moves through the ORM

graph LR
    Code["model.objects.filter(...)"]
    QS["QuerySet<br/>django/db/models/query.py"]
    Q["Q / F objects<br/>query_utils.py, expressions.py"]
    SQL["sql.Query<br/>django/db/models/sql/query.py"]
    Compiler["SQLCompiler<br/>sql/compiler.py"]
    Backend["DatabaseWrapper<br/>django/db/backends/<engine>/base.py"]
    DBMS[(SQLite / PostgreSQL /<br/>MySQL / Oracle)]

    Code --> QS
    QS --> Q
    QS --> SQL
    SQL --> Compiler
    Compiler -->|SQL string + params| Backend
    Backend --> DBMS
    DBMS -->|rows| Backend
    Backend -->|tuples| Compiler
    Compiler -->|model instances| QS
    QS -->|iterable| Code

QuerySet is the user-facing builder. Internally it owns a sql.Query object that tracks joins, filters, annotations, and ordering. When the queryset is evaluated, Query.get_compiler(using).as_sql() produces a (sql, params) tuple that the backend wrapper executes. The backend wrappers in django/db/backends/{sqlite3,postgresql,mysql,oracle}/ translate generic SQL fragments into engine-specific syntax via DatabaseOperations, DatabaseFeatures, and DatabaseSchemaEditor classes.

For deeper coverage of each subsystem, see the pages under systems/.

Built by Factory AutoWiki from public repository content. It is a generated preview for codebase exploration, not source-maintained documentation.

Architecture – Django wiki | Factory