Custom Template Tags vs. Custom Context Processors in Django

Custom Template Tags vs. Custom Context Processors in Django

Django comes with two methods for handling data or output across multiple templates: Template Tags and Context Processors. But why would you use one over the other? Let's take a look at their main strengths and some examples to help you make the choice.

Custom Template Tags

You can add template tags to use on your templates like reusable components. They are almost like an include file and a view function combined, since they can contain and insert logic and/or functionality into templates.

You are able to control the tag's HTML output, making it easy to customize the presentation of data.

The main limitation with template tags is access to the context in which they're used. You can absolutely set a new context variable or read variables from the original context, but you can't modify those original variables.

If you do set a new context variable, it will only be available to the custom tag itself, not to the wider template or even the context outside of the block the tag is used in.

Here's an example of that limitation. We'll set a new_variable in the context:

@register.simple_tag(takes_context=True)
def custom_templatetag(context):
	context['new_variable'] = 'This will not be available in the template outside of this tag' 
	return 'Tag output'

...but if we try to use that variable in our template outside of our custom_templatetag, nothing will be displayed:

{% load custom_tags %}

{% custom_templatetag %}

{{ new_variable }}  {# This will not work, 'new_variable' is not accessible here #}

Custom Context Processors

This is a quick way to make data available in all templates without needing to pass it explicitly in each view. Maybe you have a tagline or something that appears on every page of your site. Context processors allow you to add variables to the context globally, meaning these variables are available in all templates.

You now have the ability to make site-wide changes: one update to the context processor changes everywhere.

The big limitation here would be that too much data added to global context could cause performance issues. You'll have to be discerning to make sure you're not needlessly overusing custom context processors.

Quick Summary: How to Choose?

Use Custom Template Tags When:

  • You need control over HTML output.
  • You want to contain and reuse complex logic.
  • The logic is specific to a particular template or set of templates.
  • You need a view-like powered template partial

Use Custom Context Processors When:

  • You need to add data globally to the context for all templates.
  • You want to simplify template code by avoiding repetitive context data in views.
  • The logic is simple and doesn't require control over the HTML output.
  • You need to add a site-wide "default" context variable (which can be overridden on a template-basis).

Examples

Say we need to put up a cookie banner on every page if the user has not already accepted the cookie policy (indicated by setting a cookie that they accepted).

We'll use a context processor, because that way, we can insert a context variable that will determine whether we show the cookie banner include file.

# APPNAME/context_processors.py

def cookie_banner(request):
    show_cookie_banner = not request.COOKIES.get('cp')
    return {'show_cookie_banner': show_cookie_banner}
# settings.py

TEMPLATES = [
    {
        'BACKEND': 'django.template.backends.django.DjangoTemplates',
        'DIRS': ...,
        'APP_DIRS': True,
        'OPTIONS': {
            'context_processors': [
                # ... 
                'APPNAME.context_processors.cookie_banner',
            ],
        },
    },
]

In the template(s):

<!-- templates/base.html or another template -->
{% if show_cookie_banner %}
    {% include 'cookie-banner.html' %}
{% endif %}

Why context processor?

  • Gives you the ability to add the cookie check on every page
  • Could include this in your base.html template, meaning you don't need it explicitly on every possible template

Next example: There are several pages with forms on the site we're working on. All of these pages need to have our standard legal disclaimer beneath the form.

In this case, we'll use a custom template tag, because even though the content will be the same everywhere, we only need this text to show on specific templates (rather than the entire site).

from django import template

register = template.Library()

@register.simple_tag
def demo_request_disclaimer():
	return """
	<small>We probably respect your privacy.</small>
	"""
{% load APPNAME_tags %}  <!-- Load the tag library -->
<!-- ... -->
<form action="{% url 'demo_request' %}" method="post">
    <!-- ...form elements... -->
    
    {% demo_request_disclaimer %}
</form>

You can add logic either to the demo_request_disclaimer template tag itself, or surround the tag with if-statements / other logic in the HTML template as needed.

Why template tag?

  • Specific pages
  • Output was more explicit (wrapped in <small> tags)
  • Essentially a partial file, but with the ability to use python and logic