Search code examples
pythontemplatesjinja2

How can I make a simple counter with Jinja2 templates?


I have two for loops, both alike in dignity. I'd like to have a counter incremented during each inner iteration.

For example, consider this template:

from jinja2 import Template

print Template("""
{% set count = 0 -%}
{% for i in 'a', 'b', 'c' -%}
  {% for j in 'x', 'y', 'z' -%}
    i={{i}}, j={{j}}, count={{count}}
    {% set count = count + 1 -%}
  {% endfor -%}
{% endfor -%}
""").render()

Shouldn't this print count=0 through count=8? Nope, it doesn't.

i=a, j=x, count=0
i=a, j=y, count=1
i=a, j=z, count=2
i=b, j=x, count=0
i=b, j=y, count=1
i=b, j=z, count=2
i=c, j=x, count=0
i=c, j=y, count=1
i=c, j=z, count=2

What gives?

Note: I can't simply save the outer loop variable to calculate the counter because, in my software, the number of inner iterations is variable.


Solution

  • With variable inner group sizes, this will work:

    from jinja2 import Template
    
    items = [
        ['foo', 'bar'],
        ['bax', 'quux', 'ketchup', 'mustard'],
        ['bacon', 'eggs'],
        ]
    
    print Template("""
    {% set counter = 0 -%}
    {% for group in items -%}
      {% for item in group -%}
        item={{ item }}, count={{ counter + loop.index0 }}
      {% endfor -%}
      {% set counter = counter + group|length %}
    {% endfor -%}
    """).render(items=items)
    

    ...which prints:

    item=foo, count=0
      item=bar, count=1
    
    item=bax, count=2
      item=quux, count=3
      item=ketchup, count=4
      item=mustard, count=5
    
    item=bacon, count=6
      item=eggs, count=7
    

    I guess variables declared outside up more than one level of scope can't be assigned to or something.