Custom template tags in Django

Django    2008-08-15

In various places in my project's site navigation, I need to be able to include dynamic content. For example, there will be a sidebar on the left that should always show a list of categories, with each one linking to its respective category page. A pretty common need on most web sites, right? The category model is already defined in another app. Since I'll need this snippet to be available in templates throughout the project (for now it's just in the lefthand nav, but I predict we'll use it in other places) I decided to create a template tag that I can plug in anywhere. The Django documentation covers the very basics of writing custom template tags, but the example they use is for a date/time tag - they don't go into detail about how to work with objects you've defined within your project: Extending the template system (This stuff is also covered in the Django book, Chapter 10: Extending the Template Engine.) It's worth skimming over the part about custom filters - you might find yourself referring back to it later. Or you can plunge right in here: Writing custom template tags You'll notice that there are a few different ways you can write/register your custom tags: a regular tag that relies on a Node subclass, a simple_tag, or an inclusion_tag (a template tag "that displays some data by rendering another template"). The inclusion tag is exactly what I needed for my navigation piece. I started by adding a new app, named 'navigation', to my project, and adding it to the settings file:

INSTALLED_APPS = (
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.sites',
    'django.contrib.admin',
    'django.contrib.humanize',
    'myproject.registration',
    'myproject.tracking',
    'myproject.articles',
    'myproject.navigation',
)
The navigation app might never contain anything but these custom tags, and that's okay. Here's what it does need to have:
navigation/
    __init__.py
    templatetags/
        __init__.py
        menu_tags.py
	category_list.html
I put my tags in a file called menu_tags.py (this name can be whatever you choose - it's the name you'll refer to when you load the tags into a template later). Import the Django template module and register, like so:
    from django import template

    register = template.Library()
And since I'm also working with an object from elsewhere in my project, I imported that model:
    from articles.models import Category
My method couldn't be simpler:
    def nav_categorylist():
        categories = Category.objects.filter(active=1)
        return {'categories': categories}
I'm not even taking any arguments, just grabbing a list of all my active categories and returning them as a dictionary. When I register the tag, I'm rendering the results in a template called 'category_list.html' (it can live in the same templatetags/ folder - and mine does, for now). The second part binds the nav_categorylist() method to the tag:
    register.inclusion_tag('category_list.html')(nav_categorylist)
The template, also absurdly simple:
    <div>
    <div>Categories: </div>
    {% for category in categories %}
        <a href="/offerings/list/cat/{{ category.id }}/">{{ category.title }}</a><br />
    {% endfor %}
    </div>
Now the 'nav_categorylist' tag is available to any template throughout my project. All the way back up in my project's base template, where all of my content blocks are defined, I'm first loading the custom tag library like so:
    {% load menu_tags %}
Then calling the tag:
     <div id="leftnav">
        {% block left_nav %}
            {% nav_categorylist %}
        {% endblock %}
     </div>