DJango Notes

Useful or to read...

Page Contents

Create A Django Project

Creating The Project

Running from the directory in which you want to house your new project that you have named <prj-name>:

django-admin startproject <prj-name>

Creates a new directory named <prj-name> under your CWD:

  +-- prj-name/
        +-- <- The global index mapping views to URLs

Add A New App

From the project directory <prj-name>:

python startapp <app-name>

This extends the directory structure as follows:

  +-- prj-name/
  |     |
  |     +-- <- The global index mapping views to URLs
  +-- app-name/
        +-- migrations/

Then modify <prj-name>/<prj-name>/ (the global index) to point at <prj-name>/<app-name>/

Running The Development Server

From the project directory <prj-name>:

python runserver ip-address:port

Middleware & How HTTP Requests Get To A View

WSGI stands for Web Server Gateway Interface and is a specification of how a webserver can call a Python framework with an HTTP request and get a response.

A WSGI compantible framework will implement a callable object that receives as parameters the HTTPRequest object and a callback, which it uses to send the HTTP response headers to the server. The function must then return the HTTP reponse body as a list of strings.

The start of the processes, clipped in places, is shown below (I took this from the Django 1.11 source code).

DJango WSGI flow

This doesn't yet show how requests get to a view. That'll come. For now, note that the server will call a core django function, get_wsgi_application() that returns a callable object. This object is called and passed a dictionary of CGI-like parametrs (key, value pairs) and a callback function. For example the dictionary on my machine, when I printed it out, looked something like this:

<class 'dict'>
 'DJANGO_SETTINGS_MODULE': 'crossfit.settings', 
 'TZ': 'UTC',
 'SERVER_NAME': 'localhost',
 'SERVER_PORT': '8000',
 <snip> }

The parameters will create an HTTPRequest object, and the callback will be used by Django to send the server the HTTP headers that will form the reponse. The called object finally must return the HTTP response body as a list of strings.

See PEP 3333 -- Python Web Server Gateway Interface.

When get_wsgi_application() is called it returns a callable object. The function creates a new instance of a WSGIHandler object. As part of its construction the Django middleware is loaded by consulting the list of strings, defined by the variable MIDDLEWARE in the file of your Django project.

The WSGIHandler, which is a child of django.core.handlers.base.BaseHandler loads the middleware using the BaseHandler. It examimes each middleware object in MIDDLEWARE.

Each middleware object can implement some or all of the the functions

  1. process_view()
  2. process_template_response()
  3. process_exception()

If a middleware object implements a function above, that function is added to a list of middleware functions of that type that the handler object caches.

The middleware objects must, in addition to the above, form a chain. The first middleware object created will point to BaseHandler._get_response(), so it will call BaseHandler._get_response. The second middleware object points to the first middle ware and so on. Only a reference to the last middleware object therefore needs to be remembered. In this way, later on, when the middleware chain is executed, the MIDDLEWARE list is essentially executed in reverse order and the very last thing to execute is BaseHandler._get_response.

For example, the default MIDDLEWARE that Django spits out for me is this:


This means that the following middleware chain is formed:

So that is how the Django middleware is loaded. That was the arrow in the above diagram which went to "nowhere"... it was too much to fit that process into the diagram, but thats what was happening :)

Okay, so, we've loaded the middleware and created the WSGIHandler object, which is returned to the WSGI server, for it to call with the CGI-like variables (environment) and the start_response callback function.

The big bit is how the HTTP response is generated through the call from WSGIHandler to its base class function get_response(). Here the fun starts...

The WSGI server calls the WSGIHandler object. One of the first things it does is to send a Django-framework signal. Then it takes the dictionary passed to it and converts it into a WSGIRequst object. This class mostly seems to extract more important dictionary key/value pairs and presents them as object properties. For example, it might parse a GET request's query string and handle cookies etc.

The meaty bit is the next bit where the respone is processed by calling the base class method get_response().

The first thing it does it to call the middleware chain. This means the last module defined in the settings MIDDLEWARE list is called, I.e., all middleware is a callable of some kind.

It seems all the middleware include the MiddlewareMixin class which is what defines the __call__() function for the middleware object. This is found in django.utils.deprecation, so I guess they must be wanting to get rid of this pretty soon. Anyway, when a middlware is called it implements the following logic:

Given the above, it is then unsuprising that when adding a few prints into the relevant funtions we see the following output when requesting a page:

    Calling process_request SecurityMiddleware
    Calling get_response SecurityMiddleware
    Calling process_request SessionMiddleware
    Calling get_response SessionMiddleware
    Calling process_request CommonMiddleware
    Calling get_response CommonMiddleware
    Calling process_request CsrfViewMiddleware
    Calling get_response CsrfViewMiddleware
    Calling process_request AuthenticationMiddleware
    Calling get_response AuthenticationMiddleware
    Calling process_request MessageMiddleware
    Calling get_response MessageMiddleware
    Calling get_response XFrameOptionsMiddleware
VIEW view() function called
Request obj is <WSGIRequest: GET '/gym-users/'>
View dispatch(), method is "get"
    Calling process_response XFrameOptionsMiddleware
    Calling process_response MessageMiddleware
    Calling process_response CsrfViewMiddleware
    Calling process_response CommonMiddleware
    Calling process_response SessionMiddleware
    Calling process_response SecurityMiddleware

An important point is that if any middleware created a response in their process_request() method they would break the chain. This would presumably be if, for example, the security middleware detected some kind of security issue and didn't want the processing of this HTTP request to continue. Thus we can say that each middle ware, as well as potentially sanitizing the request object can also function as a gateway, either allowing the request to continue or not as it deems fit.

By passing what the various middleware objects are doing (I haven't looked at them in detail, I'm interested in the view processing here), lets continue. Finally, after all the middlewares have processed the request and found nothing illegal/dangerous the last part of the chain _get_response() is called.

It is now that a resolver is created. This is what compares the HTTP request URL with all of the configured urlpatterns. Once a match is found a match object continaing a callback, the callback's arguments and keyword arguments is returned. The callback is in fact the view associated with the url(...) that the URL matched the HTTP request URL. So, if it was a FBV then it is a function and if it is a CBV it is a class object that is callable.

Every middleware object in the _view_middleware list mentioned earlier is now called in order and passed the url-match callback and arguments. The first one to return an HTTPResonse object "wins" - that is the reponse that will be used and no views will be called.

Assuming non of the middleware caused a fuss, our view will now be called. For a FBV this is easy enough to see how - it just calls the function we wrote. For a CBV, however, we generall don't implement any specific entry point for our class. To understand how, then, our class is called we must remember we passed it to the url function using ourClass.as_view(). The function as_view() is part of the django.views.generic.base.View. It is a Django class method so can only be called on the class definition and not on a class instance. It wraps the class inside a closure that returns a function that accepts the HTTPRequest object and arguments. It is this function that encloses and instance of the view class that is then called via its dispatch() with the WSGIRequest object as its first parameter.

The dispatch() method is quite simple "router" that takes the HTTP request method, converts it to lower case, and then tries to call that method on the class. Therefore if it was a GET request, it will call the view's get() method. If it was a POST request it calls the view's post() method and so on...