skip to content

What do a Django project's wsgi.py and asgi.py contain, and how does DJANGO_SETTINGS_MODULE decide which settings they load?

level: middleimportance: must knowfreq 55%

answer

  1. a module-level callable
  2. setdefault, not assignment
  3. django.setup() runs at import
  4. WSGI_APPLICATION is read by runserver only

basics

~10 s

Each file sets a default DJANGO_SETTINGS_MODULE with os.environ.setdefault and builds a module-level application via get_wsgi_application() or get_asgi_application(). A value already in the environment wins, so each deployment picks its settings without editing the file.

solid answer

~30 s

`startproject` generates both files with the same steps: `os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'mysite.settings')`, then `application = get_wsgi_application()` or `get_asgi_application()`. Because it is `setdefault`, a `DJANGO_SETTINGS_MODULE` exported by the deployment environment wins over the file's default. Both functions call `django.setup(set_prefix=False)` — configuring logging and importing every app in `INSTALLED_APPS` — then build the handler, which loads `MIDDLEWARE` once. So importing the module is the expensive step and happens once per worker process, not per request. Servers are pointed at `mysite.wsgi:application` or `mysite.asgi:application`; the `WSGI_APPLICATION` setting is read only by `runserver`, and `runserver` never uses `asgi.py`.

code

python · 13 lines
python
import os

from django.core.asgi import get_asgi_application

os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'mysite.settings')

# Populate the app registry first ...
django_asgi_app = get_asgi_application()

# ... then import project code that may import models.
from mysite.routing import build_application  # noqa: E402

application = build_application(django_asgi_app)

go deeper

for a junior

Know that wsgi.py and asgi.py expose an object named application and set DJANGO_SETTINGS_MODULE to the project's settings by default.

for a middle

Explain setdefault precedence, what django.setup() does at import time, and why WSGI_APPLICATION matters only to runserver.

for a senior

Use the once-per-process import to reason about startup cost and restarts, and order asgi.py imports so the app registry is ready before models are imported.

for a principal

Standardise how services select settings and wrap the application object, so every deployment chooses configuration through the environment rather than edited entry files.

## The generated files `django-admin startproject mysite` writes `mysite/wsgi.py` and `mysite/asgi.py`. Stripped of their docstrings they are nearly identical: ```python import os from django.core.wsgi import get_wsgi_application os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'mysite.settings') application = get_wsgi_application() ``` `asgi.py` imports `get_asgi_application` from `django.core.asgi` instead. The contract with the server is the **module-level name `application`**: a WSGI or ASGI server imports the module and calls that object for every request. ## How DJANGO_SETTINGS_MODULE is resolved `DJANGO_SETTINGS_MODULE` is an environment variable holding the dotted path of the settings module. The order of precedence: 1. If the process environment already has `DJANGO_SETTINGS_MODULE` (exported by the container, the service manager or the shell), that value is used. 2. Otherwise the file's `os.environ.setdefault(...)` supplies the project default, `mysite.settings`. 3. `manage.py` does the same `setdefault`, and management commands also accept `--settings` to name a module for one run. 4. If nothing sets it and code touches `settings`, Django raises `ImproperlyConfigured` with *Requested setting ..., but settings are not configured*. Because environment variables are process-wide, two Django sites cannot share one process with different settings this way; Django's docs point mod_wsgi users to daemon mode per site, or to assigning `os.environ['DJANGO_SETTINGS_MODULE']` outright in `wsgi.py`. ## What the factory functions do `get_wsgi_application()` and `get_asgi_application()` are Django's public entry points; the handler classes behind them are deliberately not public API. Each one: - calls **`django.setup(set_prefix=False)`**, which loads settings, configures logging from `LOGGING` and **populates the app registry** by importing every app in `INSTALLED_APPS` and its models; - constructs the handler (`WSGIHandler` or `ASGIHandler`), which **loads the `MIDDLEWARE` chain once**, at construction; - returns that handler as the callable the server invokes per request. `set_prefix=False` is there because the URL script prefix is set per request, from the WSGI `SCRIPT_NAME` or the ASGI `root_path`. The practical consequences: importing `wsgi.py` is the slow part of starting a worker, it happens once per process, and editing `MIDDLEWARE` or `INSTALLED_APPS` needs a worker restart. ## Who reads which file | Launcher | What it loads | How it finds it | |---|---|---| | `manage.py runserver` | the WSGI callable | the `WSGI_APPLICATION` setting, default `mysite.wsgi.application`; if set to `None`, it calls `get_wsgi_application()` itself | | a WSGI server | `mysite.wsgi:application` | the path passed in its own configuration | | an ASGI server | `mysite.asgi:application` | the path passed in its own configuration | `WSGI_APPLICATION` exists only for `runserver`. There is no matching ASGI setting in Django's global settings, and `runserver` does not use `asgi.py`. ## Wrapping the application and import order Both files are the place to wrap Django in WSGI or ASGI middleware: `application = SomeMiddleware(application)` at the bottom. In `asgi.py`, when routing code for other protocols is added, keep `get_asgi_application()` **before** importing modules that import models: importing a models module before the registry is populated raises `AppRegistryNotReady` (*Apps aren't loaded yet.*). ## What interviewers listen for The `application` object, `setdefault` letting the environment win, `django.setup()` doing the heavy work once per process, and the fact that `WSGI_APPLICATION` is a `runserver` detail rather than how production servers find the app.

  • Production sets DJANGO_SETTINGS_MODULE=mysite.settings.production, but wsgi.py says mysite.settings. Which wins, and why?
    The environment wins. The generated file calls `os.environ.setdefault`, which only fills the variable when it is missing, so a value exported by the deployment is left alone. Replacing `setdefault` with a plain assignment would silently force the development default in every environment.
  • Why does a change to MIDDLEWARE not take effect until workers restart?
    `get_wsgi_application()` and `get_asgi_application()` construct the handler once per process, and the handler loads the middleware chain in its constructor. Requests reuse that chain, so a new `MIDDLEWARE` value is only read when a new process imports `wsgi.py` or `asgi.py` again.
  • What error do you get running a script that imports a Django model without setting anything up?
    Without `DJANGO_SETTINGS_MODULE`, touching settings raises `ImproperlyConfigured` saying settings are not configured. With settings but without `django.setup()`, defining or importing models raises `AppRegistryNotReady: Apps aren't loaded yet.` Set the variable and call `django.setup()` first, or write a management command, which does both for you.

saying these in an interview costs you the question

  • WSGI_APPLICATION tells Gunicorn which application to load
  • wsgi.py overrides any DJANGO_SETTINGS_MODULE exported by the environment
  • get_wsgi_application() rebuilds the middleware chain for every request
  • runserver serves asgi.py when an ASGI server is installed
  • Django reads an ASGI_APPLICATION setting to find asgi.py