18
{% set cnt = 0 %}
{% for room in rooms %}
  {% for bed in room %}
    {% set cnt = cnt + 1 %}
  {% endfor %}
{{ cnt }}
{% endfor %}

Say we have that nested loop, printed cnt will ALWAYS be 0, because that's what it was defined when we entered the 1st for loop. When we increment the counter in the inner loop, it seems to only be a local variable for the inner loop -- so it will increment while inside the loop, but then that local cnt is gone. HOW can we modify the global cnt???

As great as the Jinja2 doc may be, they are unclear about set variable scopes. The only thing mentioning scope was the "scoped" modifier for inner blocks, but I guess it can't be applied to everything ... crazy.

veryxcit
  • 419
  • 1
  • 3
  • 8
  • possible duplicate of [jinja2: get loop index of outer loop](http://stackoverflow.com/questions/1567291/jinja2-get-loop-index-of-outer-loop) – Anto Nov 26 '14 at 15:42
  • I searched for a while and found a lot of similar questions but no answers. My solution was to use a do block and a custom class that has a method which will increment a variable, since Jinja won't allow assignment statements inside a do block. So something like {% do cnt.increment() %} inside the for loop. – nonagon Dec 09 '14 at 02:57

4 Answers4

23

Scoping rules prevent you from accessing a variable declared outside a loop from inside the loop

To quote Peter Hollingsworth from his previous answer,

You can defeat this behavior by using an object rather than a scalar for 'cnt':

{% set cnt = [0] %}
{% for room in rooms %}
  {% for bed in room %}
    {% if cnt.append(cnt.pop() + 1) %}{% endif %} 
  {% endfor %}
{{ cnt[0] }}
{% endfor %}
total times through = {{ cnt[0] }}
Community
  • 1
  • 1
Chris Warth
  • 890
  • 12
  • 26
8

For each loop, there is a loop object generated which have an index attribute.

http://jinja.pocoo.org/docs/dev/templates/#for

To access parent loop index, you can do like this: http://jinja.pocoo.org/docs/dev/tricks/#accessing-the-parent-loop

Or you could use enumerate which work the same in Jinja as in Python https://docs.python.org/2/library/functions.html#enumerate

Pyglouthon
  • 552
  • 4
  • 15
  • Note that in order to use `enumerate` you would have to create a custom filter or pass in the list having already used `enumerate` on it. – qff Oct 04 '15 at 21:22
  • 1
    The pocoo link is dead. Here is the archived version: https://web.archive.org/web/20190127022412/http://jinja.pocoo.org/docs/dev/tricks/ The trick of aliasing the outer loop variable seems like the cleaner solution to me. – phantom-99w Jul 13 '21 at 08:59
  • Poco link is dead, here is the one of the current version: [Accessing the parent Loop](https://jinja.palletsprojects.com/en/3.0.x/tricks/#accessing-the-parent-loop) – Leonardo Apr 20 '23 at 16:39
1

I find the official trick pointed out by @Pyglouthon in his answer to be by far the best solution. I'll just paste it here so people don't have to browse the comments to find the working link.

From the official docs: Accessing the parent Loop

<table>
{% for row in table %}
  <tr>
  {% set rowloop = loop %}
  {% for cell in row %}
    <td id="cell-{{ rowloop.index }}-{{ loop.index }}">{{ cell }}</td>
  {% endfor %}
  </tr>
{% endfor %}
</table>
Leonardo
  • 1,533
  • 17
  • 28
-3

A very hacky way I recall using in the past:

{% set cnt = 0 %}
{% for room in rooms %}
    {% for bed in room %}
        {% if cnt += 1 %}
    {% endfor %}
{{ cnt }}
{% endfor %}

Not tested.

Oliver Fawcett
  • 583
  • 1
  • 6
  • 18