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

2 comments

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.

2602541719_cffba86acc_z

Related Posts

2 comments:

  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: http://pastebin.com/J4yLU9Bq

    ReplyDelete
    Replies
    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.

      https://docs.djangoproject.com/en/dev/howto/custom-template-tags/#assignment-tags

      Delete

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