How Django processes a request
I’ve decided to kick-start some architecture documentation for Django by describing how the core request handling mechanism in Django works. I’m talking about the part of Django that takes a request from a browser and turns it in to a response—I won’t be discussing the template system, object-relational mapper or automated admin interface, which are all separate components; in fact, you don’t need to use any of those to build a Django application.
When Django receives a request, the first thing it does is create an HttpRequest object (or subclass there-of) to represent that request. How this is done differs depending on the host environment, with different code depending on if Django is running under mod_python or WSGI (which enables hosting with CGI, Twisted and more). This HttpRequest object is the principle abstraction that enables Django to work under different hosting mechanisms.
Once the object has been created, Django performs URL resolution. This is a process by which the URL specified in the request is used to select a view function to handle the creation of a response. A trivial Django application is simply one or more view functions and a configuration file that maps those functions to URLs.
Having resolved the URL to a view, the view function is called with the request object as the first argument. Other keyword arguments may be passed as well depending on the URL configuration; see the documentation for details.
The view function is where the bulk of the work happens: it is here that database queries are made, templates loaded, HTML is generated and an HttpResponse object encapsulating the result is created. The view function returns this object, which is then passed back to the environment-specific code (mod_python or WSGI) which passes it back to the browser as an HTTP response.
This is all pretty straightforward stuff—but I skipped a couple of important details: exceptions and middleware. The view function doesn’t have to return an HttpResponse; it can raise an exception instead, the most common varieties being Http404 (for file-not-found) or Http500 (for server error). In development servers these exceptions will be formatted and sent back to the browser, while in production mode they will be silently logged and a “friendly” error message displayed.
Middleware is even more interesting. Django provides three hooks in the above sequence where middleware classes can intervene, with the middleware classes to be used defined in the site’s configuration file. This results in three types of middleware: request, view and response (although one middleware class can apply for more than one hook).
Request middleware runs after the HttpRequest object has been created but before the URL resolver runs, allowing it to modify the request in some way or to return a response of its own before the rest of the application has a chance to run.
View middleware is executed after the URL resolver has been used to identify the view but before the view itself runs. It is passed the view as a callback, allowing it to perform operations before and after the view has been executed. Alternatively, it can avoid running the view function at all.
Response middleware is run right at the end, after a response has been created but before it is passed back to the client. It has the ability to make final modifications to the HttpResponse object; for example, it could remove unnecessary whitespace from HTML or apply gzip compression.
The bulk of the above code can be found in the
__call__ method of the ModPythonHandler class and the
get_response method of the BaseHandler class. Example middleware classes can be found in the middleware directory. As Django is not yet at a 1.0 release, the above is all subject to potential
refactoring future change.