django middleware February 10, 2008
Django Middleware is used when processing all requests and responses coming in and out of an application. With middleware, you can intercept a request, change it, or even deny it. Middleware is useful if you have some sort of repetition across your view functions.
how does it work
Django's middleware is implemented via a class. You write a class, list it under settings.MIDDLEWARE_CLASSES, and any request/response will be processed by your Middleware methods. Here's an example of a settings.py file:
MIDDLEWARE_CLASSES = (
'django.middleware.common.CommonMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'myapp.blog.BlogMiddleware' # I added this line here
)
defining the middleware
A middleware class has four special methods that it can implement. If written, these methods will be called by Django for each request or response. These methods are:
process_request(self, request)
process_view(self, request, view, args, kwargs)
process_response(self, request, response)
process_exception(self, request, exception)
process_request(self, request)
First, before the request's view is even resolved, the process_request method will be called if it exists. If you return None, the request will go on as normal. If you return an HttpResponse, Django will render your response without calling a view function.
class BlogMiddleware(object):
def process_request(self, request):
from django.http import HttpResponseRedirect
""" This is a private blog, user must be logged in to do anything"""
if not request.user.is_authenticated() and request.path != "/login/":
return HttpResponseRedirect('/login/')
else:
# add a convenience variable for each request
request.is_valid_user = True
return None
The process_request methods are handy to intercept requests or to attach additional information to each request. In this case, the entire blog is only viewable for logged in users, so BlogMiddleware will deny anybody unless they're logged in. If they're logged in, BlogMiddleware will attach the is_valid_user property to the request.
process_view(self, request, view, args, kwargs)
After process_request, the process_view method is called after the view function is resolved. The parameters for process_view are request, view, args, kwargs. view is the actual view function about to be called, args and kwargs are the arguments passed to that view as a list and dictionary, respectively.
If process_view returns an HttpResponse object, that response will be used. Otherwise, Django will continue with its chain of command and call the view function.
class BlogMiddleware(object):
# ....
def process_view(self, request, view, args, kwargs):
""" gives all views an extra 'user' param in kwargs if one doesn't already exist """
if not kwargs.has_key('user'):
kwargs['user'] = request.user
return view(request, view, *args, **kwargs)
process_response(self, request, response)
The process_response method is called after the view function. Since the process_response method takes the response returned from the view as a parameter, you can modify it as you wish or return a completely different response object. This method must always return an HttpResponse object.
class BlogMiddleware(object):
# ...
def process_response(self, request, response):
""" set cookie for last known response """
response.set_cookie(key="last_response", value=request.path)
return response
process_exception(self, request, exception)
If any uncaught excepts are thrown (besides a 404), the process_exception method is your chance to catch it and act on it. This is useful for a project-wide exception handler. In this case, let's say some users aren't authorized for certain actions (ie. they cannot create or update content). If they try, let our views throw a blog.AuthorizationRequired exception and we can handle it in our Middleware.
class BlogMiddleware(object):
# ...
def process_exception(self, request, exception):
from django.http import HttpResponse
if type(exception) == blog.AuthorizationRequired:
return HttpResponse("Whoops! Please contact an administrator for authorization.")
django middleware, useful and lightweight
I find myself using process_request often so I can provide my views with additional data attached to the request object. I also use process_exception for exceptional cases that occur throughout multiple views.
Django middleware is a lightweight way to keep your code DRY and well abstracted.