Set up data for access on all Jinja templates in Django (like Django context processors)

Problem

You want to set up data to become available on all Jinja templates in a Django project, without having to set up the data individually on Django view methods.

Solution

Create a custom Jinja environment class and set data inside the globals variable. Configure your Django project to use the custom Jinja environment through the environment key, as part of OPTIONS that's part of the Jinja TEMPLATES configuration in settings.py.

How it works

Just like Django templates offer context processors to facilitate the access of data on all Django templates in a project, Jinja also offers its own version of the same feature which is called globals. Jinja in its out-of-the-box state has no globals, but in its Django integrated mode includes three globals to mimic Django's most common context processors, these globals are: request, csrf_input and csrf_token. This means you get access to three Django context processor 'like variables in all Jinja templates used in Django projects. However, to set up additional global variables you need to work with Jinja's environment.

To set up Jinja globals you need access to Jinja's environment which is where globals are stored, in a variable properly called globals. By default, the Django-Jinja configuration uses Jinja's built-in jinja2.Environment environment. In order to access Jinja's environment in Django and set globals, the easiest path is to create your own Jinja environment and use it to initialize the Django-Jinja configuration.

Listing 1 illustrate a custom Jinja environment class which sets the global variables named static and url.

Listing 1 - Custom Jinja environment with global variable

from jinja2.environment import Environment
from django.contrib.staticfiles.storage import staticfiles_storage
from django.core.urlresolvers import reverse

class JinjaEnvironment(Environment):
    def __init__(self,**kwargs):
        super(JinjaEnvironment, self).__init__(**kwargs)
        self.globals['static'] = staticfiles_storage.url
        self.globals['reverse'] = reverse
        

As you can see in listing 1, the custom JinjaEnvironment class is a sub-class of the jinja2.Environment class, this is so the custom class inherits the bulk of its features from this base class provided by Jinja. Next, you can see we use the __init__ method to initialize the base class.

Prior to exiting the initialization method of the class, you can also see we access the globals variable of the instance. globals is composed of a dictionary, where the key-values correspond to the Jinja template variable names and values, respectively.

In this case, we create the static variable and assigns it Django's django.contrib.staticfiles.storage.staticfiles_storage.url method. This effectively grants the static global variable the same behavior as Django's staticfiles app, so that it's possible to declare static resources -- as described in the recipe Set up static web page resources -- Images, CSS, JavaScript -- just like it's done in Django (e.g. Jinja can then do <img src="{{ static('images/background.png') }}" alt="Background">) -- note this is an important gap to fill given Jinja's lack of functionality on this front.

The second global in listing creates the url variable and assigns it Django's django.core.urlresolvers.reverse method. This effectively grants the url global variable the same behavior as Django's {% url %} tag, so that it's possible to resolve a URL based on a name-- as described in the recipe Name Django urls for easier management and reverse matches -- just like it's done in Django (e.g. Jinja can then do <a href="{{ url('homepage') }}">Go to homepage</a>) -- note this is another important gap to fill given Jinja's lack of functionality on this front.

Just as you can add these last two global variable to mimin the behavior of Django apps and tags that are missing in Jinja templates, you can add more globals in the same manner or increase the complexity of a Jinja global as needed.

Once the custom Jinja environment is ready, you need to set it up in Django's settings.py file so it's used to initialize Jinja. Listing 2 illustrates how to set up a custom Jinja environment in Django.

Listing 2 - Configure custom Jinja environment in Django setttings.py

TEMPLATES = [
    { 
        'BACKEND':'django.template.backends.jinja2.Jinja2',
        'DIRS': ['%s/templates/'% (PROJECT_DIR),],
        'APP_DIRS': True,
        'OPTIONS': { 
            'environment': 'coffeehouse.jinja.env.JinjaEnvironment'
            }
        },
    ]

The Jinja environment is set through the environment key, as part of the OPTIONS variable. The value of the environment key is a string with dot notation that points to a custom Jinja environment class. In this case, you can see the value corresponds to coffeehouse.jinja.env.JinjaEnvironment, where JinjaEnvironment is the class -- in listing 1 -- env is the file/module name and coffeehouse.jinja is the directory path.

To better illustrate the location of the env.py file containing the custom Jinja environment, listing 3 illustrates a directory structure with additional Django project files for reference.

Listing 3 - Directory structure and location of custom Jinja environment
 
+---+-<PROJECT_DIR_coffeehouse>
    |
    +-__init__.py
    +-settings.py
    +-urls.py
    +-wsgi.py
    |
    +-jinja-+
            +-__init__.py
            +-env.py