django: Easy way for template tags to add information to template context


I've been seeing a pattern emerging with Django template tags that I've been writing and it's starting to annoy me.

Example usage:

{% some_tag by request.user as context_var %}
{% for item in context_var %} ... {% endfor %}

The template tag would be:

from django.template import Library, Node, Variable

register = Library()

def some_tag(parser, token):
bits = token.split_contents()
tag = bits.pop(0) # pop tag
context_var = None

if len(bits):
context_var = bits.pop(0)

user_var = Variable(bits.pop())
return SomeTagNode(context_var, user_var = user_var)

class SomeTagNode(Node):
def __init__(self, context_var, user_var = None):
self.context_var, self.user_var = context_var, user_var

def render(self, context):
context[self.context_var] = SomeModel.objects.filter(user = self.user_var.resolve(context))
return ''

As you can see, there is some fluffing involved with creating a new class for something that could be done easily. The trick is finding a way to make it easier to re-use.

I found it annoying that registering tags using Library.simple_tag() automatically parses the context values (but did not allow access to it) and Library.inclusion_tag() supported the use of takes_context but the regular Library.tag() didn't.

Place this code in a re-usable file.

from django.template import Node

class ContextNode(Node):
def __init__(self, func):
self.func = func

def render(self, context):
return self.func(context)

Now if you want to create a new tag:

from django.template import Library, TemplateSyntaxError, resolve_variable

register = Library()

def some_tag(parser, tokens):
bits = token.split_contents()
tag = bits.pop(0) # pop tag
context_var = None

if len(bits):
context_var = bits.pop(0)

user_var = bits.pop()

# This is a wrapper function to accept the context variable
def some_tag_wrap(context):
context[context_var] = SomeModel.objects.filter(user = resolve_variable(user_var, context))
return ''

return ContextNode(some_tag_wrap)

This makes life much simpler if all you want to do is add a variable to the context.

Also, note the use of resolve_variable(), which gets rid of the repetitively annoying use of Variable.resolve().

Now you can go back to doing things in life that matter.



  1. Very helpful!

    For completeness, you could add an example of a simple tag that takes no arguments.

    Hmm. I dont' see how to put code into this comment field. Take a look here:

    1. Hey StatFiend,

      Depending on which version of Django you're using, there's an easier built-in way of doing things now since v1.4 called assignment tags.


Leave your thoughts ...
If you are having trouble with copy/pasting in comments, you need to sign in or click 'Preview'. For more information about this Firefox bug, see here.

Copyright © Twig's Tech Tips
Theme by BloggerThemes & TopWPThemes Sponsored by iBlogtoBlog