Django tricks: Custom context processors
In this article I am going to explore another excellent tool Django provides to us to help avoid code duplication: context processors.
Put simply, Django context processors allow us to very easily add new custom variables into the contexts across all views. These new contexts are thus “global” in that all templates across all views can access them.
One example of a context processor which comes with all Django apps is the {{ user }}
variable is which automatically included and provides access to the currently logged in User
record. This allows us to access the current user’s first name from any template using {{ user.first_name }}
for example.
When should I use a custom context processor?
Let’s imagine we wanted to implement a change to an existing large Django app to show a random inspirational quote on all of our views. Without context processors, this would involve pulling up every single view (whether they be CBVs for FBVs) and adding the same new field to all context dictionaries.
As you can imagine, this is not only a very laborious process, but it flies in the face of Django’s DRY (Don’t Repeat Yourself) principle. The example above is a great candidate for using Django’s context processor: that way we only have to implement this change once to give all our templates access to this data.
Generally, any context variables displayed on templates shared across many pages (i.e. headers or footers) is a great candidate for custom context processors.
How do I create a custom context processor?
Luckily, Django makes it a breeze to define a new context processor.
First, we create a new file named context_processors.py
in our app folder, and we create our custom processor method, below:
from django.core.handlers.wsgi import WSGIRequest
from .models import InspirationalQuotes
def some_constants(request: WSGIRequest) -> dict:
return {
'foo': 'Hello world!',
'random_quote': InspirationalQuotes.objects.order_by('?').first()
}
The new method only takes in a standard Django request object, and returns a standard dict, the same way the get_context_data
method does in most CBVs.
The dictionary entries returned by this method will be automatically inserted into the context of all our Django pages!
Next, we need to update our Django settings to add a reference to this new context processor. In thesettings.py
file, look for the TEMPLATES
definition. Inside, append to the context_processors
list with the path to your new processor:
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',
'myproject.myapp.context_processors.some_constants'
],
},
},
]
It’s that simple! Now we can start using our new contexts in all our templates! If we ever create more additional custom context processors, we continue adding them to the list above. Using the custom context processor we created above, we can now reference them in any template, for example:
<h1>This is a page!</h1>
Foo Constant: {{ foo }}<br />
Inspirational Quote: {{ random_quote.description }}
You’ll notice that in our custom context processor, we have access to the request
object, which provides information about the HTTP request, including the current user (via request.user
) and all other typical request data.
That’s it! With Django’s built-in context processors we can very quickly and very easily create “global” contexts that are accessible from all templates! While the example above is very simple, context processors have the capability to enable more complex capabilities — i.e. multi-tenancy, URL namespacing, etc.
What do you plan to use context processors for? Sound off in the comments below!