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

3

5

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

Radionuclide answered 16/3, 2009 at 15:46 Comment(0)
C
9

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.

Cabot answered 16/3, 2009 at 17:21 Comment(4)
You should include an example of the tag usage such as {% last_books 'Sports' 3 as my_books %}Matamoros
You're right, I updated the code sample with more informationCabot
Awesome, this works great. Thanks! Things are starting to make more sense to me now.Radionuclide
Ok found in the doc, "separates the arguments on spaces while keeping quoted strings together"Actiniform
F
3

Why not with?

Forrer answered 16/3, 2009 at 15:49 Comment(1)
Perhaps I'm using "with" incorrectly? When I try: {% with last_books 'Sports' 3 as book_list %} I get this error: u'with' expected format is 'value as name'Radionuclide
C
0
{% last_books sports 3 as last_books %}  # do it like this

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

but it's better to use inclusion_tag

Celiotomy answered 8/6, 2022 at 7:41 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.