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()
@register.tag
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()
@register.tag
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.