Chapter 1 · What Django is

1.3 Django’s neighbours

Django’s neighbours are the software it works with and does not contain: what calls it, what it needs, and what is built on it.

The figure draws the line between Django and its neighbours, and the calls that cross it: the server’s call into the handler, which the middleware chain carries to the view, and the database backend’s call out to its driver, made for the models and querysets the view uses.

Figure 1.4 Where Django stops. Between the two rules, Django’s code and the libraries it requires; above, the server, which calls the handler; below, the driver and the database, which the backend calls. An arrow is a call.

The server

A web server owns the socket: it accepts connections, reads HTTP, and calls one Python object it was given when it started. Under WSGI it calls that object with the request’s environ and a function that starts the response; under ASGI it awaits it with a scope and two awaitable callables for messages. The object is Django’s handler, and what it does with the call is The two entry points.

A server is pointed at the object a project’s wsgi.py or asgi.py makes with get_wsgi_application or get_asgi_application, each of which runs django.setup and makes a handler. The WSGI servers Gunicorn, uWSGI, mod_wsgi and Waitress are pointed at the first; the ASGI servers Daphne, Uvicorn and Hypercorn at the second. The ASGI handler serves HTTP only: any other scope, a WebSocket’s included, it refuses with ValueError.

Django’s own tree holds one server, the one runserver starts: WSGI only, built on the standard library’s wsgiref.simple_server, and by its docstring not for production. It serves the object WSGI_APPLICATION names, which in a new project is the one a production WSGI server is given. If the setting is None, it serves what get_wsgi_application returns. The development server takes it apart.

What Django requires

Django’s pyproject.toml names three dependencies beside Python 3.12 or newer: asgiref 3.12.1 or newer, sqlparse 0.5.0 or newer, and tzdata on Windows only. The first two are imported at the top of the modules that use them: asgiref before django.setup runs, since the module a server or manage.py starts from imports the signals, whose dispatcher imports asgiref, and sqlparse with a database backend’s operations, when the backend is loaded.

asgiref is the bridge between synchronous and asynchronous code, and Django’s uses of it fall into three groups.

sqlparse serves the database backends, in three ways. BaseDatabaseOperations.prepare_sql_script splits a script into statements for a RunSQL operation, a migration’s step of raw SQL, given one string; on PostgreSQL it is executed whole. BaseDatabaseOperations.format_debug_sql lays out a query for the test runner’s --debug-sql. And the introspection of SQLite and of MySQL parses SQL the database gives back, such as a table’s "CREATE TABLE" statement or a check constraint’s clause, to find constraints, their columns and, on SQLite, each column’s collation.

tzdata Django never imports. Its time zones are the standard library’s zoneinfo, which reads the system’s database of time zones and, where there is none, as on Windows, the tzdata package’s.

The database drivers

A database backend’s base module imports its driver, the library that talks to the database, at its top, under the name Database. The module is imported when the connection of its alias is first asked for, so a missing driver raises ImproperlyConfigured then, naming the driver, in the backends for PostgreSQL, MySQL and Oracle.

Each driver raises its own exceptions, under the names PEP 249 gives them. DatabaseErrorWrapper raises each error again as Django’s class of the same name, so that code above the backend catches one IntegrityError whatever the driver. The rest is Database backends.

The libraries a feature imports

Beyond its requirements, Django imports a library only where a feature needs it, and where the import stands decides when its absence shows. Inside a function, it shows when the function runs, as with the cache clients and the hashers’ libraries. At the top of a module Django imports on demand, it shows then, as Jinja2’s does when an engine of TEMPLATES uses its backend; PyYAML’s is caught when the serializers are first registered, and becomes a stand-in that raises when called. In a try at a module’s top, it can set a flag: without docutils, the admin’s documentation pages say so.

Table 1.3 Some of the optional libraries: what uses each, when its absence shows, and where it is imported.
library used by absence shows import
Pillow, as PIL used by ImageField, model and form; image sizes; image extensions absence shows at the call import ImageField._check_image_library_installed django/db/models/fields/files.py, ImageField.to_python django/forms/fields.py, get_image_dimensions django/core/files/images.py, get_available_image_extensions django/core/validators.py
argon2-cffi used by Argon2PasswordHasher absence shows at the call import Argon2PasswordHasher.library django/contrib/auth/hashers.py
bcrypt used by BCryptSHA256PasswordHasher, BCryptPasswordHasher absence shows at the call import BCryptSHA256PasswordHasher.library django/contrib/auth/hashers.py
redis used by RedisCache absence shows at the call import RedisCacheClient.__init__ django/core/cache/backends/redis.py
pymemcache used by PyMemcacheCache absence shows at the call import PyMemcacheCache.__init__ django/core/cache/backends/memcached.py
pylibmc used by PyLibMCCache absence shows at the call import PyLibMCCache.__init__ django/core/cache/backends/memcached.py
Jinja2, as jinja2 used by the Jinja2 template backend; require_jinja2 absence shows as the module loads; for require_jinja2, as a flag import jinja2 django/template/backends/jinja2.py, jinja2 django/test/utils.py
PyYAML, as yaml used by the YAML serializer absence shows at the call, by a stand-in import yaml django/core/serializers/pyyaml.py
docutils used by django.contrib.admindocs absence shows as a flag import docutils_is_available django/contrib/admindocs/utils.py
geoip2 used by GeoIP2 absence shows at the call import geoip2 django/contrib/gis/geoip2.py
GDAL, a C library, through ctypes.CDLL used by GeoDjango’s spatial references, rasters and vector data absence shows as the module loads import lgdal django/contrib/gis/gdal/libgdal.py
GEOS, a C library, through ctypes.CDLL used by GeoDjango’s geometries absence shows as the module loads import load_geos django/contrib/gis/geos/libgeos.py

The packages built on the extension points

A package built on Django attaches where Django leaves a point open: where it imports what a project names (How Django is extended), or outside it, around the handler or in a process of its own.

Table 1.4 Some packages built on Django: the point each attaches to, and its home.
package attaches to home
Channels attaches to around the handler, as an ASGI application that wraps it (The two entry points) home site
WhiteNoise attaches to around the WSGI application, or in MIDDLEWARE instead home site
Django Debug Toolbar attaches to MIDDLEWARE (Building the middleware chain), and URL patterns home site
django-allauth attaches to MIDDLEWARE, INSTALLED_APPS and AUTHENTICATION_BACKENDS home site
django-cors-headers attaches to MIDDLEWARE home repository
django-htmx attaches to MIDDLEWARE home site
Django REST framework attaches to views and URL patterns (Views) home site
Wagtail attaches to INSTALLED_APPS, with models (Settings, apps and startup) home site
django CMS attaches to INSTALLED_APPS, with models home site
django-extensions attaches to INSTALLED_APPS, for its commands (Finding the command) home site
django-storages attaches to STORAGES home site
django-redis attaches to CACHES home repository
django-filter attaches to forms, which it builds (Forms) home site
crispy-forms attaches to forms, which it renders through a template tag library of its own home site
pytest-django attaches to the test runner’s place: pytest runs the tests (The test framework) home site
django-environ attaches to the settings module, which calls it (The settings object) home site
Celery attaches to a process of its own, a worker that reads the settings and uses the ORM home site