djangoargumentstemplatetags

In Django, how can I iterate over the result of a custom template tag that requires arguments?


If I have created a template tag:

@register.simple_tag
def last_books(a_cat, cutoff=5):
    objects = Books.objects.filter(category=a_cat)
    return objects[:cutoff]

How can I do something like this in my template:

{% for book in last_books 'Sports' 3 %}

I am currently getting this error:

'for' statements should use the format 'for x in y': for x in last_books 'Sports' 3


Solution

  • Personally, I would simply pass in the book as a context variable via the view. That way you have no need for a template tag.

    Alternately, you could use the inclusion_tag decorator instead, which wraps up the idea of rendering an include template with a custom context into the current document.

    But if you want to continue on the current path, the simple_tag decorator isn't the way to go. It's for use when you need to return a string which you want rendered directly into the template. What you're trying to do is set a template context variable. This is a bit more involved, but not too difficult. Create a node something like this:

    class LastBooksNode(template.Node):
        def __init__(self, category, cutoff=5, var_name='books'):
            self.category = category
            self.cutoff = cutoff
            self.var_name = var_name
        def render(self, context):
            context[self.var_name] = Books.objects.filter(category=self.category)[:self.cutoff]
            return ''
    
    @register.tag(name='last_books')
    def do_last_books(parser, token):
        error = False
        try:
            tag_name, category, cutoff, _as, var_name = token.split_contents()
            if _as != 'as':
                error = True
        except:
            error = True
    
        if error:
            raise TemplateSyntaxError, 'last_books must be of the form, "last_books <category> <cutoff> as <var_name>"'
        else:
            return LastBooksNode(a_cat, cutoff, var_name)
    

    You would then invoke the template tag with:

    {% import <your tag library> %}
    {% last_books 'category' 5 as my_books %}
    {% for book in my_books %}
        ....
    {% endfor %}
    

    Untested, but I hope this demonstrates the idea. As mentioned above, though, passing the books directly to the view via the context or using an inclusion_tag may be easier if you don't intend to reuse this in multiple places.