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 | +-- prj-name/ | +-- urls.py <- The global index mapping views to URLs
Add A New App
From the project directory <prj-name>:
python manage.py startapp <app-name>
This extends the directory structure as follows:
prj-name | +-- prj-name/ | | | +-- urls.py <- The global index mapping views to URLs | +-- app-name/ | +-- admin.py +-- apps.py +-- models.py +-- tests.py +-- views.py +-- migrations/ +-- urls.py
Then modify <prj-name>/<prj-name>/urls.py (the global index) to point at <prj-name>/<app-name>/urls.py.
Running The Development Server
From the project directory <prj-name>:
python manage.py 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).
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'> {'<snip> 'DJANGO_SETTINGS_MODULE': 'crossfit.settings', 'TZ': 'UTC', 'SERVER_NAME': 'localhost', 'GATEWAY_INTERFACE': 'CGI/1.1', 'SERVER_PORT': '8000', <snip> 'REQUEST_METHOD': 'GET', <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
settings.py
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
process_view()
process_template_response()
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:
MIDDLEWARE = [ 'django.middleware.security.SecurityMiddleware', 'django.contrib.sessions.middleware.SessionMiddleware', 'django.middleware.common.CommonMiddleware', 'django.middleware.csrf.CsrfViewMiddleware', 'django.contrib.auth.middleware.AuthenticationMiddleware', 'django.contrib.messages.middleware.MessageMiddleware', 'django.middleware.clickjacking.XFrameOptionsMiddleware', ]
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:
SecurityMiddleware Calling process_request SecurityMiddleware Calling get_response SecurityMiddleware SessionMiddleware Calling process_request SessionMiddleware Calling get_response SessionMiddleware CommonMiddleware Calling process_request CommonMiddleware Calling get_response CommonMiddleware CsrfViewMiddleware Calling process_request CsrfViewMiddleware Calling get_response CsrfViewMiddleware AuthenticationMiddleware Calling process_request AuthenticationMiddleware Calling get_response AuthenticationMiddleware MessageMiddleware Calling process_request MessageMiddleware Calling get_response MessageMiddleware XFrameOptionsMiddleware Calling get_response XFrameOptionsMiddleware _GET_RESPONSE CALLED NO MIDDLE WARE RESPONSE 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...