Backend line
Django lifecycle
Every Django page follows one fixed path, like an order in a restaurant, so you can tell which step is to blame when something fails.
Stops explain each step. The film plays the whole ride.
- Order going in
- Side note
- Dish coming back
- Something went wrong
A customer sends an order form
A customer fills in an order form and hands it to the host at the door. This form changes something in the kitchen, so it will be checked carefully.
Next stop: The form goes past the front-desk team
All stops
The everyday version
A busy restaurant works the same way. Every stop has a twin there.
- The customeris the Browser
Fills in the form and waits for the dish.
- The host at the dooris the WSGIHandler
Takes the form, tidies it into a slip, and later carries the tray back.
- The security deskis the Session + auth
Knows the customer, and looks up the member card only when someone asks.
- The pass checkeris the CsrfViewMiddleware
Looks for the restaurant’s own stamp on the form before the chef sees it.
- The seating hostis the URL resolver
Reads the address and sends the order to the right chef.
- The chefis the View
Does the real work: cooks, and decides what the answer is.
- The pantryis the Database
Keeps every ingredient and hands over only what is asked for.
- The plating stationis the Template
Fills a ready-made layout with today’s details to make the finished page.
- A turned-away orderis a 403, 404 or 500 error
Sometimes the form is stopped at the desk, sometimes no page fits, and sometimes the chef drops the dish. Each time the customer gets a short note instead.
Interview questions
Say the short answer first. Go deeper only if they ask.
What happens between the server and your view?
The server builds an HttpRequest, middleware wraps it, the URL resolver picks a view, and the view returns an HttpResponse that travels back out.
A WSGI server calls the application callable, which is a WSGIHandler that builds a WSGIRequest. Middleware runs top-down, then Django resolves the URL, calls each process_view, and runs the view. The response returns through the same layers in reverse, and WSGIHandler passes it to start_response.
“Middleware only runs after the view”, or forgetting that URL resolution comes before process_view.
In what order does middleware run?
Top-down through MIDDLEWARE on the way in, and in reverse on the way out.
It is an onion: each layer wraps the next. process_view also runs top-down, but process_exception and process_template_response run bottom-up. A layer that returns early skips everything inside it. A new project lists Security, Session, Common, Csrf, Authentication, Messages and XFrameOptions.
“Every hook runs in the order I listed it”, or putting AuthenticationMiddleware before SessionMiddleware.
Why does the CSRF check run in process_view?
It runs after URL resolution, so it can see whether the matched view is csrf_exempt, and before the view does anything.
GET, HEAD, OPTIONS and TRACE are not checked. A POST needs a csrfmiddlewaretoken form field (from {% csrf_token %}) or an X-CSRFToken header. It also checks Origin, or Referer on HTTPS when Origin is missing, and CSRF_TRUSTED_ORIGINS lists extra allowed origins. Failure returns 403, and the test client skips the check unless enforce_csrf_checks=True.
“Add @csrf_exempt to make the 403 go away.”
When does a QuerySet hit the database?
Not when you build it. Only when something evaluates it: iterating, list(), len(), bool(), slicing with a step, or pickling.
Each filter() returns a new lazy QuerySet. Once evaluated, a QuerySet caches its rows, so reuse the variable instead of calling Dish.objects.all() again. Prefer exists() and count() over bool() and len(), and select_related or prefetch_related to fetch related rows up front instead of one query per row.
“filter() runs the query”, or “calling Dish.objects.all() twice is free”.
How does request.user get set?
AuthenticationMiddleware sets it as a lazy object that reads the session only when you first use it.
It needs SessionMiddleware listed before it, or Django raises an error. request.user is a SimpleLazyObject that calls get_user(request) on first access and caches the result. A visitor who is not logged in gets AnonymousUser. In async views use await request.auser().
“request.user is always loaded before the view runs”, or listing the auth middleware without sessions.
What is the difference between WSGI and ASGI in Django?
startproject creates both wsgi.py and asgi.py, each exposing an application callable. WSGI servers call the first, ASGI servers the second.
WSGI servers such as Gunicorn, uWSGI and Granian call WSGIHandler, and ASGI servers such as Daphne, Hypercorn, Uvicorn and Granian call ASGIHandler. Both build the HttpRequest, load the same MIDDLEWARE and return a response. Django’s default ASGI handler runs your code in a synchronous thread.
“runserver is fine for production.”
How do 403, 404 and 500 pages come about?
Django turns exceptions into responses: PermissionDenied gives 403, Http404 (including Resolver404) gives 404, and any other uncaught exception gives 500.
Built-in views render 403.html, 404.html and 500.html, and handler403, handler404 and handler500 replace them. A failed CSRF check has its own view, CSRF_FAILURE_VIEW. With DEBUG on you get debug pages instead. The django.request logger records 5xx as ERROR and 4xx as WARNING.
“Leave DEBUG=True in production to see errors.”
Cheat-sheet
Commands you will actually type.
python manage.py runserverStart the development server on 127.0.0.1:8000. It reloads on code changes. Never use it in production.
gunicorn myproject.wsgi
python -m uvicorn myproject.asgi:applicationServe over WSGI or ASGI. Run from the folder that holds manage.py.
python manage.py check --deployRun the extra checks meant for a production deployment.
def timing(get_response):
def middleware(request):
# before the view
response = get_response(request)
# after the view
return response
return middlewareA middleware factory. Code before `get_response` runs on the way in, and code after it on the way out.
<form method="post">{% csrf_token %}Every POST form that targets your own site needs the token. For AJAX, send the `X-CSRFToken` header.
from django.db import connection
connection.queriesList the SQL run so far, with timings. Only recorded when `DEBUG` is `True`.
with self.assertNumQueries(1):
list(Dish.objects.filter(name="biryani"))Fail a test if the block runs a different number of queries.
handler404 = "mysite.views.my_custom_page_not_found_view"Set in your URLconf to replace the default 404 view. 403, 500 and 400 have matching handlers.
Sources
- Django: Middleware
- Django: Built-in middleware reference
- Django: URL dispatcher
- Django: Request and response objects
- Django: Writing views
- Django: Built-in error views
- Django: Shortcut functions (render)
- Django: Cross Site Request Forgery protection
- Django: How to use Django’s CSRF protection
- Django: QuerySet API reference
- Django: Making queries
- Django: The Django template language
- Django: TemplateResponse
- Django: Exceptions
- Django: Settings
- Django: Logging reference
- Django: django-admin and manage.py
- Django: Deploying with WSGI
- Django: How to use Django with Gunicorn
- Django: Deploying with ASGI
- Django: How to use Django with Uvicorn
- Django: Testing tools
- Django FAQ: seeing the raw SQL
- Django: URL utilities (handler404 and friends)
- Django source: core/wsgi.py
- Django source: core/handlers/base.py
- Django source: core/handlers/wsgi.py
- Django source: core/handlers/asgi.py
- Django source: core/handlers/exception.py
- Django source: views/csrf.py
- Django source: middleware/csrf.py
- Django source: contrib/auth/middleware.py
- Django source: contrib/sessions/middleware.py
- Django source: project template settings