View method middleware
In most circumstances, data in
requests & responses is added, removed or updated in a
piecemeal fashion in view methods. However, sometimes it's
convenient to apply these changes on all requests and
For example, if you want to
access certain data on all view methods, it's easier to use a
middleware class to make this data accessible across all requests.
Just as if you want to enforce a security check on all responses,
it's easier to do so globally with a middleware class.
Since middleware is a rather
abstract concept, before I describe the structure of a Django
middleware class, I'll walk you through the various built-in Django
middleware classes so you can get a firmer understanding of where
middleware is good design choice.
Built-in middleware classes
Django comes equipped with a
series of middleware classes, some of which are enabled by default
on all Django projects. If you open a Django project's
settings.py file you'll notice the
MIDDLEWARE variable whose default contents are shown
in listing 2-25.
Listing 2-25 Default Django middleware classes in
MIDDLEWARE = [
As you can see in listing 2-25,
Django projects in their out-of-the-box state come enabled with
seven middleware classes, so all requests and responses are set to
run through these seven classes. If you plan to leverage Django's
main features, I advise you not to remove any of these default
middleware classes. However, you can leave the
MIDDLEWARE variable empty if you wish, just be aware
doing so may break certain Django functionalities.
To give you a better
understanding of what the Django middleware classes in listing 2-25
do and help you make a more informed decision to disable them or
not, table 2-6 describes the functionality for each of these
Table 2-6 Django default middleware classes and
|Provides security enhancements, such as:
- SSL redirects based on the SECURE_SSL_REDIRECT and SECURE_SSL_HOST settings. Strict transport security through a variety of settings.
|Enables session support.
|Provides a common set of features, such as:
- Forbidding access to user agents in the DISALLOWED_USER_AGENTS
setting, which can be a list of compiled regular expression
- Performing url rewriting based on the APPEND_SLASH and
PREPEND_WWW settings in order to normalize urls.
- Setting the HTTP Content-Length header for non-streaming responses.
|Adds protection against Cross Site Request Forgeries by
adding hidden form fields to POST forms and checking requests for
the correct value.
|Adds the user attribute, representing the
currently-logged-in user, to every incoming HttpRequest object.
NOTE: This middleware class depends on functionality from the
django.contrib.sessions.middleware. and must appear after it.
|Enables cookie-based and session-based message support.
NOTE: This middleware class depends on functionality from the
django.contrib.sessions.middleware.SessionMiddleware and must appear after it.
|Provides clickjacking protection via the X-Frame-Options
header. For more details on what is clickjacking see: http://en.wikipedia.org/wiki/Clickjacking.
As you can see in table 2-6,
although the purpose of the various default middleware classes
varies considerably, their functionality applies to features that
need to be applied across all requests or responses in a
Another important factor of the
middleware classes in table 2-6 is that some are dependent on
others. For example, the
class is designed on the assumption it will have access to
functionality provided by the
Such dependencies are important because it makes the middleware
class definition order relevant (i.e. certain middleware classes
need to be defined before others in
topic I'll elaborate on more in the next section.
In addition to the default
middleware classes presented in table 2-6, Django also offers other
middleware classes. Table 2-6 illustrates the remaining set of
Django middleware classes you can leverage in your projects, which
can be helpful so you don't have to write middleware classes from
Table 2-7 Other Django middleware classes and functionality
|Response-phase cache middleware that updates the cache if
the response is cacheable. NOTE:
UpdateCacheMiddleware must be the
first piece of middleware in
MIDDLEWARE so that it'll get
called last during the response phase.
|Request-phase cache middleware that fetches a page from
the cache. NOTE:
FetchFromCacheMiddleware must be the last piece of
MIDDLEWARE so that it'll get called last during the request phase.
|Sends broken link notification emails to
|Django uses this middleware regardless of whether or not
you include it in
MIDDLEWARE, however, you may want to subclass if
your own middleware needs to transform the exceptions it handles
into the appropriate responses.
|Compresses content for browsers that understand GZip
GZipMiddleware should be placed before any other
middleware that need to read or write the response body so that
compression happens afterward. Compression is only done by this
middleware if the request party sends gzip on the HTTP
Accept-Encoding header and if content is larger than 200 bytes and
the response hasn't set the HTTP Content-Encoding
|Handles conditional GET operations. If the response
doesn't have an HTTP ETag header, one is added. If the response has
an ETag or Last-Modified header, and the request has If-None-Match
or If-Modified-Since, the response is replaced by an
|Parses a request and decides what translation object to
install in the current thread context. This allows pages to be
dynamically translated to the language the user desires.
|Adds the site attribute representing the current site to
REMOTE_USER -- available in
request.META -- via an
external source (e.g. web server) for the purpose of Django authentication.
|Allows web-server provided authentication. If
is not authenticated, this middleware attempts to authenticate the
username passed in the
REMOTE_USER request header. If
authentication is successful, the user is automatically logged in
to persist the user in the session.
|Each time a Django application raises a 404 error, this middleware checks the flatpages database for the requested url as a
|Each time a Django application raises a 404 error, this
middleware checks the redirects database for the requested url as a
Now that you know about Django's
built-in middleware classes and what it's they're used for, lets
take a look at the structure of middleware classes and their
Middleware structure & execution process
A Django middleware class has two
required methods and three optional methods that execute at
different points of the view request/response life-cycle. Listing
2-26 illustrates a sample middleware class with its various
Listing 2-26. Django middleware class structure
def __init__(self, get_response):
self.get_response = get_response
# One-time configuration and initialization on start-up
def __call__(self, request):
# Logic executed on a request before the view (and other middleware) is called.
# get_response call triggers next phase
response = self.get_response(request)
# Logic executed on response after the view is called.
# Return response to finish middleware sequence
def process_view(self, request, view_func, view_args, view_kwargs):
# Logic executed before a call to view
# Gives access to the view itself & arguments
def process_exception(self,request, exception):
# Logic executed if an exception/error occurs in the view
def process_template_response(self,request, response):
# Logic executed after the view is called,
# ONLY IF view response is TemplateResponse, see listing 2-21
In order for view methods to
execute the Django middleware class in listing 2-26, middleware
classes must be added to the
MIDDLEWARE variable in
settings.py. So for example, if the
CoffeehouseMiddleware class in listing 2-26 is stored
in a file/module named
middleware.py under the
coffeehouse/utils/ project folders, you would add the
statement to the list of
MIDDLEWARE values in
Next, I'll describe the two
required methods in all Django middleware class show In listing
__init__.- Used in all Python classes to bootstrap
object instances. The
__init__ method in Django
middleware classes only gets called once, when the web server
backing the Django application starts. The
method in Django middleware must declare a
get_response input, which represents a reference to a
prior middleware class response. The
input is assigned to an instance variable -- also named
get_response -- which is later used in the main
processing logic of the middleware class. The purpose of the
get_response reference should become clearer shortly
when I expand on the Django middleware execution process.
__call__.- Used in all Python classes to call an
object instance as a function. The
__call__ method in
Django middleware classes is called on every application request.
As you can see in listing 2-26, the
request input which represents the same
HttpRequest object used by view methods. The
__call__ method goes through three phases:
- Before view method call.- Once the
__call__ method is triggered,
you get the opportunity to alter the
before it's passed to a view method. If you want to add or
modify something in
request before it gets turned over
to a view method, this is the phase to do it in.
- Trigger view method call.- After you modify (or not) the original
you must turn over control to the view method in order for it to
run. This phase is triggered when you pass
self.get_response reference you set in the
__init__ method. This phase effectively says "I'm done
request, go ahead and turn it over to
the view method so it can run".
- Post view method call.- Once a view method finishes, the results are assigned to the
response reference in
__call__. In this
phase, you have the opportunity to perform logic after a view
method finishes. You exit this phase by simply returning the
response reference from the view method (i.e.
This is the core logic behind
every Django middleware class performed by these two required
methods. Now let's take a look at the three optional middleware
class methods presented in listing 2-26:
process_view.- The required middleware methods --
__call__ -- lack any
knowledge about the view method they're working on. The
process_view method gives you access to a view method
and its argument before the view method is triggered. If
process_view middleware method is invoked
__call__ and before calling
self.get_response(request) which triggers the view
process_exception.- If an error occurs in the
logic of a view method, the
middleware method is invoked to give you the opportunity to perform
post-error clean up logic.
self.get_response(request) is called and a view method
finishes, it can be necessary to alter the response itself to
perform additional logic on it (e.g. modify the context or
template). If present, the
middleware method is invoked after a view method finishes to give
you the opportunity to tinker with the response.
process_template_response middleware method is only triggered if a
view method returns a
TemplateResponse. If a view method generates
a response with
process_template_response is not
triggered. See listing 2-21 for view method responses and additional details about using
In summary, the execution process
for a single middleware class is the following:
__init__ method triggered (On server
__call__ method triggered (On every request).
- If declared,
process_view() method triggered.
- View method starts with
- If declared,
process_exception() method triggered
when exception occurs in view.
- View method finishes.
- If declared,
when view returns
Although it's important to
understand the execution process of a single middleware class, a
more important aspect is to understand the execution process of
multiple middleware classes. As I mentioned at the outset of this
section, Django projects are enabled with seven middleware classes
shown in listing 2-25, so the execution of multiple middleware
classes is more the norm rather than the exception.
Django middleware classes are
executed back-to-back, but the view method represents an inflection
point in their execution order. The execution order for the default
middleware classes in listing 2-25, is the following:
__init__ on django.middleware.security.SecurityMiddleware called
__init__ on django.contrib.sessions.middleware.SessionMiddleware called
__init__ on django.middleware.common.CommonMiddleware called
__init__ on django.middleware.csrf.CsrfViewMiddleware called
__init__ on django.contrib.auth.middleware.AuthenticationMiddleware called
__init__ on django.contrib.messages.middleware.MessageMiddleware called
__init__ on django.middleware.clickjacking.XframeOptionsMiddleware called
A request for index() view method
__call__ on django.middleware.security.SecurityMiddleware called
process_view on django.middleware.security.SecurityMiddleware called (if declared)
__call__ on django.contrib.sessions.middleware.SessionMiddleware called
process_view on django.contrib.sessions.middleware.SessionMiddleware called (if declared)
__call__ on django.middleware.common.CommonMiddleware called
process_view on django.middleware.common.CommonMiddleware called (if declared)
__call__ on django.middleware.csrf.CsrfViewMiddleware called
process_view on django.middleware.csrf.CsrfViewMiddleware called (if declared)
__call__ on django.contrib.auth.middleware.AuthenticationMiddleware called
process_view on django.contrib.auth.middleware.AuthenticationMiddleware called (if declared)
__call__ on django.contrib.messages.middleware.MessageMiddleware called
process_view on django.contrib.messages.middleware.MessageMiddleware called (if declared)
__call__ on django.middleware.clickjacking.XframeOptionsMiddleware called
process_view on django.middleware.clickjacking.XframeOptionsMiddleware called (if declared)
Start index() view method logic
If an exception occurs in index() view
process_exception on django.middleware.clickjacking.XframeOptionsMiddleware called (if declared)
process_exception on django.contrib.messages.middleware.MessageMiddleware called (if declared)
process_exception on django.contrib.auth.middleware.AuthenticationMiddleware called(if declared)
process_exception on django.middleware.csrf.CsrfViewMiddleware called (if declared)
process_exception on django.middleware.common.CommonMiddleware called (if declared)
process_exception on django.contrib.sessions.middleware.SessionMiddleware called (if declared)
process_exception on django.middleware.security.SecurityMiddleware called (if declared)
If index() view returns TemplateResponse
process_template_response on django.middleware.clickjacking.XframeOptionsMiddleware called (if declared)
process_template_response on django.contrib.messages.middleware.MessageMiddleware called (if declared)
process_template_response on django.contrib.auth.middleware.AuthenticationMiddleware called(if declared)
process_template_response on django.middleware.csrf.CsrfViewMiddleware called (if declared)
process_template_response on django.middleware.common.CommonMiddleware called (if declared)
process_template_response on django.contrib.sessions.middleware.SessionMiddleware called (if declared)
process_template_response on django.middleware.security.SecurityMiddleware called (if declared)
Notice the execution order for
middleware classes prior to entering the execution of the view
method, follows the declared order (i.e. first declared runs first,
last declared last). But once the view method is executed, the
middleware execution order is inverted (i.e. last declared runs
first, first declared last).
This behavior is similar to a
corkscrew, where to get to the center (view method) you move in one
direction (1 to 7) and to move out you go in the opposite direction
(7 to 1). Therefore the middleware methods
process_template_response execute in the opposite
Visually the execution process
for the default Django middleware classes in listing 2-25 is
illustrated in figure 2-3.
Figure 2-3. Django middleware execution process