Django Middleware: Types, Examples, and CUSTOM Middleware

Middlewares are hooks to modify Django requests or response objects. You can use middleware if you want to modify the request i.e HttpRequest object which is sent to the view. Or you might want to modify the HttpResponse object returned from the view. Both these can be achieved by using middleware.

How does Django middleware work

Middleware in Django is a hook or a plugin system for altering request and response objects.

For example, let's say you have two types of users in your application i.e., free and paid.

There are few URLs or views that a free user cannot access, so in this case either you can handle free users at every view or you can create a middleware that can handle all the free users in one place.

Now, let's discuss the types of middleware in Django.

Types of Middleware in Django

Django's middleware can be divided into 2 types: built-in and custom.

Built-in Middleware: These are the default middlewares that come with Django. Few of the built-in middlewares are:

  • Cache middleware

  • Common middleware

  • GZip middleware

  • Message middleware

  • Security middleware

  • Session middleware

  • Site middleware

  • Authentication middleware

  • CSRF protection middleware

Some default middleware from settings.py.

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',
]

Custom Middleware: These are the middleware that a user creates for their purpose.

Now let's check out some of the built-in middleware.

Examples of Middleware in Django

Here, you will learn about a few of the built-in middlewares. This will help you create your custom middleware in the coming section.

Authentication middleware

Authentication middleware adds the user attribute, representing the logged-in user, to every incoming request object. If the user is not logged in then it will be set as an AnonymousUser object.

Session middleware

Session middleware helps you to store arbitrary data on a per-user basis like username, email, etc on the server side.

On every request and response, a session_id is attached to the cookies that you can check in your browser through the console.

Every time a request is made, session middleware gets the stored session data for the particular session_id and attaches it to the request object.

Read more about the session middleware from the official doc.

Message middleware

Message middleware is used to display some notification message aka flash message to the user after form submission.

You can store the message to the request object in your view and then show the message on your front end. You can check out official documentation for detailed usage.

CSRF middleware

CSRF middleware prevents Cross-Site Request Forgery attacks by adding hidden fields like CSRF_TOKEN to the POST forms and later validating for the correct value.

Now let's focus on another important aspect of middleware i.e., ordering

Ordering of middleware in Django

Order of the middleware is very important because a middleware can depend on the other middleware. For example Authentication Middleware depends on Session Middleware to store the authenticated user in session (request.session['user']) therefore, it must be placed after session middleware.

Custom Middleware in Django

**Custom middleware in Django is created either as a function style that takes a get_response callable or a class-based style whose call method is used to process requests and responses. It is created inside a file middleware.py. A middleware is activated by adding it to the MIDDLEWARE list in Django settings. **

Very often you would have used request.user inside the view. Django wants user attribute to be set on request before any view executes. Django takes a middleware approach to accomplish this. So Django provides an AuthenticationMiddleware which can modify the request object.

A middleware factory is a callable that takes a get_response callable and returns a middleware. A middleware is a callable that takes a request and returns a response, just like a view.

A middleware can be written in two ways

The first step is to create a file midleware.py inside your app. Mine structure looks like this after creating the file

custom_middleware/
├── admin.py
├── apps.py
├── __init__.py
├── middleware.py
├── migrations
│   └── __init__.py
├── models.py
├── tests.py
└── views.py

Now You can follow any of the two ways of creating a middleware as a class or as a function.

Django Middleware: As a function

You can use this function based middleware code snippet inside middleware.py

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.
        print("before response")
        response = get_response(request)
        print("After response")
        # Code to be executed for each request/response after
        # the view is called.

        return response

    return middleware

Django Middleware: As a Class

You can use this class based middleware code snippet inside middleware.py

class SimpleMiddleware:
    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.

        print("before response")
        response = self.get_response(request)
        print("After response")
        # Code to be executed for each request/response after
        # the view is called.

        return response

Activating Custom Middleware

Activating a middleware is very simple, you have to add it inside the MIDDLEWARE list in settings.py. In the middleware list, each middleware component is represented by a string that is the full python path of the middleware. You can add your SimpleMiddleware at the last of the middleware list.

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',

    'custom_middleware.middleware.SimpleMiddleware', # Class based custom Middleware
    'custom_middleware.middleware.simple_middleware', # Function based custom Middleware
]

Order of the middleware is very important because a middleware can depend on the other middleware. For example Authentication Middleware depends on Session Middleware to store the authenticated user in session (request.session['user']) therefore, it must be placed after session middleware.

After adding your middleware, run your server and visit the URL http://localhost:8080/

python manage.py runserver 0:8080

Now check your terminal for the print statements.

Django version 3.2, using settings 'conf.settings'
Starting development server at http://0:8080/
Quit the server with CONTROL-C.
before response
After response

If you can see the print statements before response and After response, it means you have successfully created your own custom Django middleware.

You can check out this middleware.py file and all the implementation. You can also contribute new or different ways of making and working with middlewares in the same above repository.

Now, you can build a basic middleware but there is more about how Django's middleware works. Therefore let's understand the middleware structure.

Django Middleware Structure

First of all, middleware is a python class or object that is capable of processing each request and response object. Here is a complete structure of a middleware:

class SimpleMiddlewareStructure:

    def _init_(self, get_response):
        self.get_response = get_response

    def _call_(self, request):

        # Code that is executed in each request before the view is called

        response = self.get_response(request)

        # Code that is executed in each request after the view is called
        return response

    def process_view(self, request, view_func, view_args, view_kwargs):
        # This code is executed just before the view is called
        pass

    def process_exception(self, request, exception):
        # This code is executed if an exception is raised
        pass

    def process_template_response(self, request, response):
        # This code is executed if the response contains a render() method
        return response

Let's understand the structure and lifecycle of middleware.

You can also overwrite these methods instead of __call__ method:

  • __init__(get_response)

  • __call__()

  • process_view(request, view_func, view_args, view_kwargs)

  • process_exception(request, exception)

  • process_template_response(request, response)

Let's cover each method one by one.

Only the first two methods, __init__ and __call__, are required by the Django framework for your middleware to work properly. The other three are special hooks that allow you to invoke your middleware under specific conditions. Therefore, the other three are optional.

Method 1: init() in middleware

The first method, init, is the constructor for our Python class. It is called only once, at the time the server starts up. It takes get_response as a parameter, which is passed by the Django itself when we add it inside the Middleware list like this: 'custom_middleware.middleware.SimpleMiddleware'

Method 2: call() in middleware

__call__() method is called once per request .This method invokes the middleware. Here is what it looks like:

def _call_(self, request):

        # Code that is executed in each request before the view is called

        response = self.get_response(request)

        # Code that is executed in each request after the view is called
        return response

You can add validation or feature to the request or response object before or after the view is called.

Method 3: process_view() in middleware

process_view() is called just before Django calls the view.

It should return either None or an HttpResponse object.

  • If it returns None, Django will continue processing this request, executing any other process_view() middleware and, then, the appropriate view.

  • If it returns an HttpResponse object, Django won’t bother calling the appropriate view; it’ll apply response middleware to that HttpResponse and return the result.

Method 4: process_exception() in middleware

Django calls process_exception() when a view raises an exception.

process_exception() should return either None or an HttpResponse object.

  • If it returns an HttpResponse object, the template response and response middleware will be applied and the resulting response returned to the browser.

  • Otherwise, default exception handling kicks in.

Method 5: process_template_response() in middleware

process_template_response() is called just after the view has finished executing if the response instance has a render() method, indicating that it is a TemplateResponse or equivalent.

It must return a response object that implements a render method. It could alter the given response by changing response.template_name and response.context_data, or it could create and return a brand-new TemplateResponse or equivalent.

That's all about middleware. I hope you like it.

Did you find this article valuable?

Support Nitin Raturi by becoming a sponsor. Any amount is appreciated!