Custom template tags in Django

Posted by: barbara | Date: Aug 15, 2008 | Updated: Sep 09, 2008 | Category: Django    
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>
Comment by Kenny Meyer on Feb 02, 2010:
This is exactly what I was searching for... Thanks! Good writing.
Comment by stefan on May 08, 2010:
herman, you forgot to {% load menu_tags %} in template
Comment by slogger on Jul 08, 2010:
I've tried this, but my Categories.objects.filter queryset doesn't seem to update on a particular view unless the whole django app is reloaded.
Comment by zhaiduo on Sep 21, 2010:
very helpful, thanks.
Comment by alan on Jan 24, 2011:
Awesome! heheh that was exactly what i needed right now. Simplest and probably one of the most useful templatetags, when you already have created template file and just need to render it at some place.
Comment by Rob Clark on Jul 05, 2011:
Hi, I don't normally comment on Blogs but you've got so much spam here I thought I would. Thanks for posting the info on custom tags, I'm having a few issues getting my app working at the moment but it looks from your post that I'm at least going in the right direction. Cheers -Rob