---
title: "DRY: Template Rendering with Context Processors"
description: "Mariusz Felisiak explores how to simplify template rendering with context processors in Django."
---

# DRY: Template Rendering with Context Processors

![Dark blue background with colorful doodles, flying balloons, and a pink hair pony.](https://fly.io/django-beats/dry-template-rendering-with-context-processors/assets/dry-template-rendering-with-context-processors-cover.webp)

[Annie Ruygt](https://annieruygtillustration.com/)

Mariusz Felisiak, a Django and Python contributor and a Django Fellow, explores how to simplify template rendering with context processors. Django on Fly.io is pretty sweet! Check it out: [you can be up and running on Fly.io in just minutes.](https://fly.io/docs/django/)

Django has many hidden gems that are neither widely used nor well known. In this article, I will discuss [context processors](https://docs.djangoproject.com/en/stable/topics/templates/#context-processors) which are one of my favorites. They allow following the DRY ( *don’t repeat yourself* ) principle in template rendering and keep your code more maintainable. Let’s check how it works and why it’s worth using.

## Let’s get some context

[Context](https://docs.djangoproject.com/en/stable/ref/templates/api/#playing-with-context) is a dictionary-like object that a [template](https://docs.djangoproject.com/en/stable/ref/templates/api/#django.template.Template) renders together with a request. In most cases we pass it as a standard dictionary to the `render()` shortcut. For example:

```
# views.py

from django.shortcuts import render

def my_hello_view(request):
    greetings = "Welcome aboard ⛵"
    return render(request, "my_app/hello.html", context={"greetings": greetings})
```

```
<!-- hello.html -->

{{ greetings }}
```

So far, so good, but what if we have information that should be available for every template. How do we inject them without rewriting everything and repeating ourselves? 🤔

Context processors are helpers designed for exactly this. They are functions that accept a request and return a dictionary that is populated to the template context when any template is rendered. See the following stub:

```
def my_context_processor(request):
    ...
    return {"key": value}
```

The list of used context processors is controlled by the `"context_processors"` option of the `TEMPLATES` setting.

Django includes many [built-in context processors](https://docs.djangoproject.com/en/stable/ref/templates/api/#context-processors), but we can also implement our own. To put it all together, let’s see a full example.

## Full example

We need an existing or new Django project. Here are some great resources for [getting started with Django](https://www.djangoproject.com/start/) or [deploying your Django app to Fly.io](https://fly.io/docs/django/getting-started/#initial-set-up).

With a project ready, let’s get started!

Let’s assume that we want to share information about the current version and environment of our project for all templates. Environment can be defined as a [setting](https://docs.djangoproject.com/en/stable/ref/settings):

```
# my_project/settings.py

ENVIRONMENT = "staging"
```

and version is stored in the project:

```
# my_project/__init__.py

VERSION = (2, 23, 1, "rc", 2)

__version__ = ".".join(str(part) for part in VERSION)
```

We can define a new context processor called `metadata()` that will add both information to the template context:

```
# my_project/context_processors.py

from django.conf import settings

from . import __version__

def metadata(request):
    return {
        "version": __version__,
        "environment": settings.ENVIRONMENT,
    }
```

Then we add the dotted Python path to it (`"my_project.context_processors.metadata"`) to the list of context processors:

```
# my_project/settings.py

...

TEMPLATES = [
    {
        "BACKEND": "django.template.backends.django.DjangoTemplates",
        "DIRS": [],
        "APP_DIRS": True,
        "OPTIONS": {
            "context_processors": [
                "django.template.context_processors.debug",
                "django.template.context_processors.request",
                "django.contrib.auth.context_processors.auth",
                "django.contrib.messages.context_processors.messages",
                # Our context processor:
                "my_project.context_processors.metadata",
            ],
        },
    },
]
```

That’s it! Information about `version` and `environment` are now available for all templates 🥳

We can use them, for example, to display a header about the current version:

```
<!-- templates/main.html -->

<!DOCTYPE html>
<html lang="en" dir="ltr">
  <head>
    ...
    <style>
        .version-header {
            text-align: center;
            font-size: 1.3em;
            font-weight: 400;
            padding: 20px;
        }
        .staging-header {
            background-color: yellow;
            color: black;
        }
        .development-header {
            background-color: green;
            color: white;
        }
    </style>
  </head>
  <body>
    {% if environment != "production" %}
        <div class="{{environment}}-header version-header">
            {{ environment|capfirst }} environment, version {{ version }}.
        </div>
    {% endif %}
    ...
  </body>
</html>
```

Believe me, it has saved me many times from making a mistake on a production environment. This is what it looks like in the development version when the `ENVIRONMENT` setting is set to `"development"`:

![Staging Environment](https://fly.io/django-beats/dry-template-rendering-with-context-processors/assets/template-rendering-with-context-processor-staging.webp?2/3&center)

and this is how it appears on the staging when the `ENVIRONMENT` setting is set to `"staging"`:

![Development Environment](https://fly.io/django-beats/dry-template-rendering-with-context-processors/assets/template-rendering-with-context-processor-dev.webp?2/3&center)

## Django really flies on Fly.io

You already know Django makes it easier to build better apps. Well now Fly.io makes it easier to _deploy_ those apps and move them closer to your users making it faster for them too!

[Deploy a Django app today! →](https://fly.io/docs/django/)

## Closing thoughts

Context processors are really handy for sharing information between templates and I believe they should be used more widely.

[Built-in context processors](https://docs.djangoproject.com/en/stable/ref/templates/api/#context-processors) mainly provide different settings to the templates, but there are more sophisticated uses, such as providing an instance of currently logged-in `user` with its permission by `django.contrib.auth.context_processors.auth`, or passing token needed by [CSRF](https://docs.djangoproject.com/en/stable/ref/csrf/) ( *Cross Site Request Forgery* ) protection by `django.template.context_processors.csrf`. Sky’s the limit 🚀

Do you already have an idea how to use them in you project? 💡 Try them and share!
