What do a Django project's wsgi.py and asgi.py contain, and how does DJANGO_SETTINGS_MODULE decide which settings they load?
answer
- a module-level callable
- setdefault, not assignment
- django.setup() runs at import
- WSGI_APPLICATION is read by runserver only
basics
~10 sEach 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 linesimport 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
Know that wsgi.py and asgi.py expose an object named application and set DJANGO_SETTINGS_MODULE to the project's settings by default.
Explain setdefault precedence, what django.setup() does at import time, and why WSGI_APPLICATION matters only to runserver.
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.
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