Django Middleware Understanding Django 1.10 middleware's new style


Django 1.10 introduced a new middleware style where process_request and process_response are merged together.

In this new style, a middleware is a callable that returns another callable. Well, actually the former is a middleware factory and the latter is the actual middleware.

The middleware factory takes as single argument the next middleware in the middlewares stack, or the view itself when the bottom of the stack is reached.

The middleware takes the request as single argument and always returns an HttpResponse.

The best example to illustrate how new-style middleware works is probably to show how to make a backward-compatible middleware:

class MyMiddleware:

    def __init__(self, next_layer=None):
        """We allow next_layer to be None because old-style middlewares
        won't accept any argument.
        self.get_response = next_layer

    def process_request(self, request):
        """Let's handle old-style request processing here, as usual."""
        # Do something with request
        # Probably return None
        # Or return an HttpResponse in some cases

    def process_response(self, request, response):
        """Let's handle old-style response processing here, as usual."""
        # Do something with response, possibly using request.
        return response

    def __call__(self, request):
        """Handle new-style middleware here."""
        response = self.process_request(request)
        if response is None:
            # If process_request returned None, we must call the next middleware or
            # the view. Note that here, we are sure that self.get_response is not
            # None because this method is executed only in new-style middlewares.
            response = self.get_response(request)
        response = self.process_response(request, response)
        return response