Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

like image 736
Steve Avatar asked Dec 18 '22 08:12

Steve


1 Answers

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.

like image 169
Daniel Naab Avatar answered Dec 31 '22 13:12

Daniel Naab