Chapter 1 · A request, end to end
1.1 Before the first request
What a process does between importing Django and serving its first request: django.setup loads the settings and populates the app registry, which imports every model, and then the handler is made and builds its middleware chain. All of it happens once.
A Django process is not ready to serve a request the moment Django is imported. Importing django defines a version and the function django.setup, and nothing else. What makes the process one that can answer requests is a sequence of three things, run once, in this order: the settings are loaded, the applications and their models are imported and registered, and a handler is made. A server starts that sequence by importing the project’s wsgi.py, which calls get_wsgi_application. This is the recording of a process doing so, with the applications that django-admin startproject installs and one application of its own, journal, which has one model.
The process starts: a server imports journal/wsgi.py
get_wsgi_application
setup
LazySettings._setup: the settings module "journal.settings" is imported
configure_logging
Apps.populate
AppConfig.create("django.contrib.admin")
AppConfig.create("django.contrib.auth")
AppConfig.create("django.contrib.contenttypes")
AppConfig.create("django.contrib.sessions")
AppConfig.create("django.contrib.messages")
AppConfig.create("django.contrib.staticfiles")
AppConfig.create("journal")
AppConfig.import_models (admin)
Apps.register_model(contenttypes.ContentType)
Apps.register_model(admin.LogEntry)
AppConfig.import_models (auth)
Apps.register_model(auth.Permission)
Apps.register_model(auth.Group_permissions)
Apps.register_model(auth.Group)
Apps.register_model(auth.User_groups)
Apps.register_model(auth.User_user_permissions)
Apps.register_model(auth.User)
AppConfig.import_models (contenttypes)
AppConfig.import_models (sessions)
Apps.register_model(sessions.Session)
AppConfig.import_models (messages)
AppConfig.import_models (staticfiles)
AppConfig.import_models (journal)
Apps.register_model(journal.Entry)
AdminConfig.ready (admin)
SimpleAdminConfig.ready (admin)
autodiscover
AuthConfig.ready (auth)
ContentTypesConfig.ready (contenttypes)
AppConfig.ready (sessions)
MessagesConfig.ready (messages)
StaticFilesConfig.ready (staticfiles)
AppConfig.ready (journal)
WSGIHandler.__init__
BaseHandler.load_middleware
MiddlewareMixin.__init__ (XFrameOptionsMiddleware)
MiddlewareMixin.__init__ (MessageMiddleware)
MiddlewareMixin.__init__ (AuthenticationMiddleware)
MiddlewareMixin.__init__ (CsrfViewMiddleware)
MiddlewareMixin.__init__ (CommonMiddleware)
MiddlewareMixin.__init__ (SessionMiddleware)
MiddlewareMixin.__init__ (SecurityMiddleware)
get_wsgi_application does two things: it calls django.setup, and it returns a new WSGIHandler. django.setup itself is three lines: configure logging, set the script prefix (the part of the path the server mounted Django under) when asked to, and populate the app registry from settings. Everything else in the recording is what those lines set off.
The settings are loaded on first use
The settings module is not imported by django.setup. It is imported by the first line of django.setup touching a setting, which is the moment the recording marks with LazySettings.
django is a LazySettings, made when django.conf is imported and empty until something reads an attribute of it. The first read calls LazySettings, which takes the module’s dotted name from the environment variable "DJANGO_SETTINGS_MODULE" and makes a Settings from it; if the variable is not set and nobody has called LazySettings, it raises ImproperlyConfigured with the familiar message about settings not being configured. Settings first copies every upper-case name of django/ onto itself, then imports the project’s module and copies its upper-case names over them, remembering which ones the project set explicitly. It also checks that the five settings which must be lists or tuples are, and, where the platform allows, moves the time zone into the environment. Every value read through the proxy is then kept on the proxy itself, so a setting is looked up once per process.
This is why the settings module may be imported by the first of many things: a management command, a test runner, or a stray import of a module that reads a setting at import time. Whichever comes first does it, and the rest find it done. The settings object has the proxy in full.
The app registry is populated in three phases
Apps.populate is where a project’s applications become objects Django can ask about. It holds a lock, returns at once when it has already run, raises if it is entered again while running, and runs three phases over settings in order.
- Each entry becomes an
AppConfig.AppConfig.createimports the entry’s module and looks for anappssubmodule with oneAppConfigsubclass in it; a dotted path to a configuration class is accepted too. Labels must be unique. After this phaseApps.apps_readyis true, and the registry can say which applications are installed; the models modules are the next phase’s work. - Each application’s models are imported.
AppConfigimports the application’s.import_models modelsmodule if it has one. Importing it runs each model’s class statement, and a model’s class statement is where the ORM does its setup: the metaclassModelBasebuilds the class’s.__new__ _meta, anOptions, moves each field onto it, gives the class its three exceptions, DoesNotExist, MultipleObjectsReturned and NotUpdated, adds a default manager namedobjectsif the class declares none, and finally registers the class with the registry;ModelBase: a class statement becomes a model tells each of those steps.Appsfiles it under its application label and lower-case name, which is the key.register_model Apps.get_modellater answers to. The order in the recording is the order in which class statements finish. The admin’s models module imports the content types model, so that one is registered during the admin’s turn and nothing is left for its own application’s; and the through table of a many-to-many field, auth’s Group_permissions among them, is a model that the field makes while its own model’s fields are being set up, so it is registered before the model that declares the field. After this phaseAppsis true..models_ready - Each application’s
readyruns.AppConfig.readyis the hook for work that needs every model to exist. The admin’s is the one with a visible effect:AdminConfigcalls.ready autodiscover, which imports theadminmodule of every installed application so that theirModelAdminregistrations run. The auth application’s connects receivers topost_migrate, so that permissions are created after each migration, and registers system checks. After this phaseApps.readyis true.
The order of the phases is the reason an application’s models module must not touch another application’s models at import time through the registry, and the reason ready exists: in phase two the registry is not complete, and in phase three it is. Populating the app registry records what the registry answers during each.
The handler is made, and the chain with it
With the registry ready, get_wsgi_application returns a WSGIHandler. Its constructor ends by calling BaseHandler, which imports each class named in settings and instantiates it, last first, each given the layer before it as the one it will call, the last in the setting being given the handler’s own BaseHandler, so that the first in the setting is the outermost layer. Handlers and middleware explains the chain; what matters here is when the constructors run. A middleware’s constructor is startup code. SecurityMiddleware reads nine settings into attributes when it is made and never again; SessionMiddleware imports the session engine named by settings and keeps the engine’s session store class, which for the default engine is the SessionStore of django/.
What is still not done
Three things one might expect at startup are deferred to the first request that needs them, and the next sections show each happening.
- The root URLconf is not imported. The resolver is made on the first request, and it imports
settingsthe first time its patterns are asked for..ROOT_URLCONF - No template engine exists. The engines named in
settingsare made the first time a template is asked for, and each engine’s loaders the first time it looks for one..TEMPLATES - No database connection is open. A connection is opened by the first query that needs a cursor.
Under the development server all three come earlier. The runserver command runs the system checks before it serves: the check of the URL configuration asks the resolver for its patterns, which imports the URLconf, and the check of the templates asks for every engine, which makes them; it then looks for unapplied migrations, which opens a database connection, and closes every connection before it serves. The server then obtains the handler by importing the object settings names, which is the same wsgi.py a production server would import, and hands it to a WSGIServer to serve for ever. A management command’s process runs django.setup too, before the command, so the first two stages above are the same for a command as for a server; the third is a server’s, and runserver’s, and the test client makes a handler of its own to drive the chain without a server. The development server follows the command and the server it runs.