Building a Django middleware (injecting data into a view’s context)
Tags: javascript, python, django
Published: 2019-12-13 10:55:35

---
title: "Building a Django middleware (injecting data into a view’s context)"
published: true
description: "In this post you’ll learn how to build your own Django middleware and how to inject data into a view’s context directly from the middleware."
tags: javascript, python, django
canonical_url: https://www.valentinog.com/blog/django-middleware/
---

_In this post you'll learn how to build your own Django middleware and how to inject data into a view's context directly from the middleware._

{% youtube R8KWnyFM8gc %}

## What is a Django middleware and what is used for?

I had an interesting use case recently where I needed to **inject dynamic data into a Django view's context**.

The data didn't come from the database. I needed to serve different objects **depending on the request META HTTP_ACCEPT_LANGUAGE**, and to make that data accessible from a JavaScript frontend.

Building a Django middleware has been the natural solution. A **Django middleware is like a plug-in that you can hook into the Django's request/response cycle**.

In this post you'll learn how to build your own Django middleware and how to inject data into a view's context directly from the middleware.

## Setting up the project

Create a new folder for the Django project and move into it:

```bash
mkdir make-me-middleware && cd $_
```

Once inside create a Python virtual environment and activate it:

```bash
python3 -m venv venv
source venv/bin/activate
```

Next up install Django:

```bash
pip install django
```

and create the new Django project:

```bash
django-admin startproject make_me_middleware .
```

Finally create a new Django app, I'll call mine **middleware_demo**:

```bash
django-admin startapp middleware_demo
```

And now let's get to work!

## Building the Django middleware

A **Django middleware can live inside a Python class** implementing at least **two dunder methods**: init and call.

In a Django middleware **init** is called once the server starts, while **call** is called for every new request to the Django application.

With this knowledge in hand create a new file called **middleware.py** in **middleware_demo** and create a new middleware named **JSONTranslationMiddleware**:

```python
# file: middleware_demo/middleware.py

class JSONTranslationMiddleware:
    def __init__(self, get_response):
        self.get_response = get_response

    def __call__(self, request):
        response = self.get_response(request)
        return response
```

You can also see that init takes **get_response**, while **call returns the same object after taking request as a parameter**.

This step is important to make the Django app work. **get_response** in fact will be the actual view or just another middleware in the chain.

The init method can have one-time configurations and instance variables as well, in my case I declared a Python dictionary with a couple other nested dictionaries:

```python
# file: middleware_demo/middleware.py

class JSONTranslationMiddleware:
    def __init__(self, get_response):
        self.get_response = get_response
        self.translations = {
            "en": {"greeting": "Hello", "header": "Welcome Django!"},
            "nl": {"greeting": "Hallo", "header": "Welkom Django!"},
        }

    def __call__(self, request):
        response = self.get_response(request)
        return response
```

In the next section you'll see where the magic happens ...

## Template responses and middleware hooks

A **middleware can have hooks**, that is, class methods which intercept Django responses or views during their lifecycle.

My requirements were clear: I needed to inject **self.translations** into the view's context.

(The real app is more complex and loads translations from multiple files).

For those new to Django, **the context is any data that the view should render to the user**.

Luckily the middleware offers an hook made for context manipulation: **process_template_response**. It takes request and response, and has access to the context through **response.context_data**.

Here's the implementation:

```python
# file: middleware_demo/middleware.py

class JSONTranslationMiddleware:
    def __init__(self, get_response):
        self.get_response = get_response
        self.translations = {
            "en": {"greeting": "Hello", "header": "Welcome Django!"},
            "nl": {"greeting": "Hallo", "header": "Welkom Django!"},
        }

    def __call__(self, request):
        response = self.get_response(request)
        return response

    def process_template_response(self, request, response):
        response.context_data["translations"] = self.translations
        return response
```

Since **process_template_response has access to the request** you can query any key on **request.META**.

Imagine I want to serve **self.translations\["nl"\]** only if the user has the dutch language in the Django HTTP_ACCEPT_LANGUAGE header. Here's how it would look like:

```python
# file: middleware_demo/middleware.py

class JSONTranslationMiddleware:
    def __init__(self, get_response):
        self.get_response = get_response
        self.translations = {
            "en": {"greeting": "Hello", "header": "Welcome Django!"},
            "nl": {"greeting": "Hallo", "header": "Welkom Django!"},
        }

    def __call__(self, request):
        response = self.get_response(request)
        return response

    def process_template_response(self, request, response):
        if "nl" in request.META["HTTP_ACCEPT_LANGUAGE"]:
            response.context_data["translations"] = self.translations
            return response
        return response
```

Only the sky is your limit with a middleware.

Hold tight, in the next section **we'll wire up all the pieces together**.

## But wait ...

If you're an intermediate Django developer you might argue that a middleware is just too much, **I could have checked request.META directly in the view**.

But the point of having a middleware is a clear separation of concerns, plus the ability to **plug the middleware in** as needed.

## Setting up the view and the url

Open up **make_me_middleware/urls.py** and include the urls from middleware_demo:

```python
# file: make_me_middleware/urls.py

from django.contrib import admin
from django.urls import path, include

urlpatterns = [
    path("admin/", admin.site.urls),
    path("", include("middleware_demo.urls")),
]
```

Then create a new file called **urls.py** in **middleware_demo**:

```python
# file: middleware_demo/urls.py

from django.urls import path
from .views import index

urlpatterns = [
    path("demo/", index),
]
```

Finally let's create a Django view with **TemplateResponse**:

```python
# file: middleware_demo/views.py

from django.template.response import TemplateResponse


def index(request):
    context = {}
    return TemplateResponse(request, "middleware_demo/index.html", context=context)
```

This view is a bit different from a stock Django view like those you can see in the [introductory tutorial](https://docs.djangoproject.com/en/3.0/topics/http/views/). 

It uses **TemplateResponse**, a special helper which is **hooked by process_template_response from the middleware**.

**TemplateResponse** does not return any data back to the user until it reaches the middleware.

To touch the result by hand let's finally create a Django template.

## Building a Django middleware: setting up the template

My Django template is an humble HTML page, yet with an interesting addition: the **json_script Django filter**.

Starting from any key from the context json_script creates a new script tag inside the page, with the desired **id**.

Create a new folder named **middleware_demo/templates/middleware_demo** and inside it create **index.html**:

```html
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1" />
    <title>Make me a Django middleware</title>
</head>
<body>
<div id="root"></div>
</body>
{{ translations|json_script:"translations" }}
```

As the last step we're going to activate the middleware in a minute.

## Activating the middleware

First things first open up **make_me_middleware/settings.py** and enable the app:

```python
INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    # ENABLE THE NEW APP
    'middleware_demo.apps.MiddlewareDemoConfig'
]
```

Then enable the middleware:

```python
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",
    # ENABLE THE NEW MIDDLEWARE
    "middleware_demo.middleware.JSONTranslationMiddleware",
]
```

Keep in mind that **middlewares are run from top to bottom when there is a request to the Django application** and from **bottom to top when the response leaves the app**.

Now run the server:

```bash
python manage.py runserver
```

and visit the page http://127.0.0.1:8000/demo/. Open up a browser's console and you should **have access to the dictionary, now a JavaScript object** which has **been injected by the middleware**:

![The JavaScript object](https://www.valentinog.com/blog/wp-content/uploads/2019/12/django-middleware-context-view.png)

Now you can parse the text with JSON.parse as well as accessing any key on the object:

```javascript
JSON.parse(document.getElementById('translations').textContent).en.greeting
```

Great job!

## Conclusion

In this tutorial you learned how to **create a Django middleware**, how to inject data into the context, and how to use json_script in your templates.

I really hope you learned something new! **Django middlewares are the right place for abstracting custom functionality that needs to alter the request/response cycle** of a Django application.

Keep your Django apps clean.

Thanks for reading and stay tuned!

## Resources

[Django documentation for middlewares](https://docs.djangoproject.com/en/3.0/topics/http/middleware/)

[Django json_script documentation](https://docs.djangoproject.com/en/3.0/ref/templates/builtins/#json-script)

[Source code for the project](https://github.com/valentinogagliardi/make-me-middleware)

If you want to dig deeper into Django and JavaScript check out [Django REST with React](https://www.valentinog.com/blog/drf/).

Originally published on [my blog](https://www.valentinog.com/blog/django-middleware/).