Think of template context processors as behind the scenes view methods that expose data inside your templates. Let's see how to write one of them.

Let's suppose you have this Django model:

#!/usr/bin/env python
# -*- coding:utf-8 -*-

from django.db import models

class Show(models.Model):
    """ show class """
    name = models.CharField(max_length=50)
    slug = models.CharField(max_length=50, unique=True)
    description = models.TextField()
    is_active = models.BooleanField(default=True)

    def __str__(self):
        return self.name

    def get_absolute_url(self):
        """ absolute url to show instance """
        return reverse('catalog:show', args=(self.slug,))


Let's suppose you want to be able to retrieve all active shows, whatever the template you're currently on.

<h3>Active Shows</h3>
{% for s in active_shows %}
<a href="{{ s.get_absolute_url }}">{{ s.name }}</a><br />
{% endfor %}


You can process an active_shows object in every views you write, but that will be a lot to type and a lot to maintain, especially if you have lots of views. The best way to do, would be to write a piece of code so that active_shows object is automatically added to the current Context.

A context is very similar to a Python dictionary, it's a key value mapping that you pass to a template. The template will render the context by replacing all the variables by the values coming from the context. Let's see a quick example.

>>> from django.template import Template, Context
>>> t = Template("My name is {{ name }}.")
>>> c = Context({'name':'foo'})
>>> t.render(c)
'My name is foo.'
>>>


A custom Django context processor method is just a regular Python function taking an HttpRequest object as argument and returning a simple dictionary. Let's write the active_shows function, inside the context_processors.py module:

#!/usr/bin/env python
# -*- coding:utf-8 -*-

""" custom context processors """

from catalog.models import Show

def active_shows(request):
    """ context processors returning currently active shows """
    return {'active_shows': Show.objects.filter(is_active=True),
            'request': request}


Our context processor is ready, now we should tell Django where to find it by modifying the settings.py module, and updating the TEMPLATES variable:

# https://docs.djangoproject.com/en/1.9/topics/templates/

TEMPLATES = [
    {
        'BACKEND': 'django.template.backends.django.DjangoTemplates',
        'DIRS': [os.path.join(BASE_DIR, "templates"),],
        '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 custom context processor
                'utils.context_processors.active_shows',
            ],
        },
    },
]


Now, you can always have access to the active shows inside any of your templates.

For going further, you may be interested in writing performance sensitive code, so you should use the Django SimpleLazyObject class. The idea here, is:

  • to defer the call to our function until the object is actually required.
  • to cache the result after an effective call of our function.

This is how to update update our function:

#!/usr/bin/env python
# -*- coding:utf-8 -*-

""" custom context processors """

from django.utils.functional import SimpleLazyObject
from catalog.models import Show

def active_shows(request):
    """ context processors lazy returning currently active shows """

    def lazy_shows():
        return Show.objects.filter(is_active=True)

    return {'active_shows': SimpleLazyObject(lazy_shows),
            'request': request}


Little reminder here: Show.objects.filter(is_active=True) is a QuerySet. QuerySets are already lazy right? So, yes, my example is not very well adapted, yet I hope you see the point.