Take the 2-minute tour ×

I am trying to make a custom template tag that will increment a variable. That would be used like this:

{% for fruit in basket %}
    {if fruit.is_apple %}{% count apples %}{% endif %}
{% endfor %}
<p>There are {{ apples }} apples in your basket</p>

I came up with this:


class CounterNode(template.Node):
    def __init__(self, varname):
        self.varname = varname

    def render(self, context):
        if self.varname in context:
            context[self.varname] += 1
            context[self.varname] = 1
        return ''

def counter(parser, token):
        tag_name, args = token.contents.split(None, 1)
    except ValueError:
        raise template.TemplateSyntaxError(
            "'counter' node requires a variable name.")
    return CounterNode(args)

This works fine until you try to use the counter tag within a for loop. The count variable increments inside the loop, but gets reset to 1 when the variable is called outside the loop.

This has to do with the render() method of the template.defaulttags.ForNode class, which calls context.pop() at the end, but I am not able to grasp why this is done and how it can be dealt with within my custom template tag.

So the question is: how could I get my counter tag to increment even through for loops?

share|improve this question
Do you need that calculation in the template? –  Seitaridis Oct 1 '11 at 6:58
It doesn't really matter what tag is used. The problem is that any variable that is set within a forloop is not available outside the forloop. –  Marconius Oct 1 '11 at 14:38
There still are situations when you need simple counter through all template. E.g. i have a problem - when generating Kindle ebook Navigation control file (NCX) file, I need counter for sequential PlayOrder attribute. As some of the content for file is generated in {% for %} loops I cannot use this, otherwise quite useful, templatetag. –  jki Nov 10 '11 at 14:03
add comment

3 Answers

Scrap the count tag and create either a model method that counts fruit or pass the count via the view method. Templates are not really intended for business logic, even if it as simple as counting.

share|improve this answer
add comment

I found django-templateaddons library that has {% counter %} tag that works on a template level independent of nested loops.

share|improve this answer
add comment

I have a technique that works well for limiting behavior within nested loops that doesn't require any custom tags. The only limitation is that you must know in advance the upper limit you're trying to set. In our case, we knew we wanted to only display the first six image galleries out of an arbitrary number of publishers, and galleries per publisher. It's strictly a presentation layer limit, so we didn't want to special-case the view. The major caveat is that you must have n+1 values in the cycle tag to insure nothing repeats. Yes, I know I'm referring to 'mycycle' before it is declared, but since you can't really declare a variable inside the Django template language, I think I can be forgiven; it works great.

{% for pubs in sortedpubs %}
    {% for gallery in pubs.publisher.galleries.all %}
        {# Code to count inside nested loops... #}
        {# Uses "cycle" from the next line to count up to 6, then stop rendering #}
        {% if mycycle < 6 %} 
            <!-- {% cycle 1 2 3 4 5 6 7 as mycycle %} -->
            {# ...Render Stuff here... #}
        {% endif %}
    {% endfor %} 
{% endfor %}
share|improve this answer
add comment

Your Answer


