(Comments)
Django's middleware framework allows the developers to hook into the request/response processing. They allow us to alter the request/response globally on every request/response cycle. For example the AuthenticationMiddleware alters and associates the request object with a user (using sessions) for each request, which is passed on to the views.
Django's middleware has changed quiet a bit in the version 1.10. Prior to version 1.10, there were 5 hooks. The process_request(), process_view(), process_exception(), process_template_response() and process_response(). Any class that implements any of these methods can act as a middleware. All the middleware are specified in the setting MIDDLEWARE_CLASSES
which is a list of full class paths (strings).
The first two of the methods, process_request(), process_view() of middleware are executed before the execution of the view ie., that is before the request is built and passed to the view. The next three methods, process_exception(), process_template_response() and process_response() are executed once the view returns the response object and they process the response object.
While the old middleware is a class, the new middleware is any callable which takes a get_response
callable and returns a callable that takes the request and returns a response. These middlewares are specified in the MIDDLEWARE
setting. The skeleton of such a middleware will look like this:
def simple_middleware(get_response): # One-time configuration and initialization. def middleware(request): # Code to be executed for each request before # the view (and later middleware) are called. response = get_response(request) # Code to be executed for each request/response after # the view is called. return response return middleware
This is function that returns another function which will process the request and response. Similarly, the middleware can be a class that implements the __call__
method, which would look like this:
class SimpleMiddleware(object): def __init__(self, get_response): self.get_response = get_response # One-time configuration and initialization. def __call__(self, request): # Code to be executed for each request before # the view (and later middleware) are called. response = self.get_response(request) # Code to be executed for each request/response after # the view is called. return response
Both the examples above are from the official Django documentation. The get_response
can be either the view that returns the response or the next middleware in the list. It is a view only if the middleware is the last one in the list. This forms a call stack of the middleware in the order specified in the settings, hence the request is processed in the specified order of the middleware and the response in the reverse order.
To activate our middleware all we need to do is to add the middleware path in the MIDDLEWARE
setting.
This example is inspired from the django oauth toolkit which uses middleware to authenticate requests and integrate it with other Django's authentication methods. Let us take a look how it would look:
def oauth_authentication_middleware(get_response): def middleware(request): bearer_token = request.META.get("HTTP_AUTHORIZATION", "") if bearer_token.startswith("Bearer"): user = get_user_from_token(bearer_token) if user: request.user = request._cached_user = user return get_response(request) return middleware
Then we just need to add the method's path in the MIDDLEWARE setting.
MIDDLEWARE = [ '...', # Other middleware 'my_app.middlware.oauth_authentication_middleware', '...', ]
I think the function is self-explanatory. The function takes the get_response
callable and returns another function named middleware
. The middleware function checks the HTTP_AUTHORIZATION
header and reads the bearer token. Based on the bearer token an authorized user is identified. Once a user is identified the model is added as an attribute of the request (we should actually use an authentication backend to identify the user to set other flags like is_authenticated()
). Now this user is available in all the requests passed to a view.
This method (middleware) is executed every time a request is made.
The old middleware are classes as we have already discussed. For the old style middleware to work with the new style also it must extend the django.utils.deprecation.MiddlewareMixin
. The MiddlewareMixin
will turn the class object into a callable. The mixin class is defined as follows.
class MiddlewareMixin(object): def __init__(self, get_response=None): self.get_response = get_response super(MiddlewareMixin, self).__init__() def __call__(self, request): response = None if hasattr(self, 'process_request'): response = self.process_request(request) if not response: response = self.get_response(request) if hasattr(self, 'process_response'): response = self.process_response(request, response) return response
It just implements the __call__
method which in turn calls the process_request()
and process_response()
in the order as required by the new middleware. The following code could be useful to define the middleware that works on both old (prior version 1.10) and the new versions of Django.
try: from django.utils.deprecation import MiddlewareMixin except ImportError: MiddlewareMixin = object
Once we decide which class we need to extend (MiddlewareMixin
is not available in the older versions of Django), we can build our middleware class as follows:
class MyMiddlewareClass(MiddlewareMixin): def process_request(self, request): # Process the request pass def process_response(self, request, response): # Process the response return response
This is work with both the older and new middleware styles.
We develop web applications to our customers using python/django/angular.
Contact us at hello@cowhite.com
Comments