119

I would like to do something like:

variable p is from test.py which is a list ['a','b','c','d']

{% for i in p %}
{{variable++}}
{{variable}}

result output is:
1 2 3 4

Jason Aller
  • 3,541
  • 28
  • 38
  • 38
user422100
  • 2,099
  • 6
  • 22
  • 22

8 Answers8

200

You could use loop.index:

{% for i in p %}
  {{ loop.index }}
{% endfor %}

Check the template designer documentation.

In more recent versions, due to scoping rules, the following would not work:

{% set count = 1 %}
{% for i in p %}
  {{ count }}
  {% set count = count + 1 %}
{% endfor %}
Riccardo T.
  • 8,907
  • 5
  • 38
  • 78
Zach Kelling
  • 52,505
  • 13
  • 109
  • 108
121

After 2.10, to solve the scope problem, you can do something like this:

{% set count = namespace(value=0) %}
{% for i in p %}
  {{ count.value }}
  {% set count.value = count.value + 1 %}
{% endfor %}
Patrick José Pereira
  • 1,613
  • 2
  • 11
  • 12
  • 5
    I get the error: TemplateSyntaxError at /listing/ Invalid block tag on line 145: 'set', expected 'endblock'. Did you forget to register or load this tag? On: {% set count = namespace(value=0) %} Any idea of the cause of this? – Andrew Oct 22 '20 at 20:36
  • In case you are working with django like me, do note that django does not support `set`. I ended up solving it by defining a function within django model to do the calculation, as mentioned in this answer https://stackoverflow.com/a/48158461/4824639. – cylim Nov 21 '22 at 06:54
66

As Jeroen says there are scoping issues: if you set 'count' outside the loop, you can't modify it inside the loop.

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

{% set count = [1] %}

You can now manipulate count inside a forloop or even an %include%. Here's how I increment count (yes, it's kludgy but oh well):

{% if count.append(count.pop() + 1) %}{% endif %} {# increment count by 1 #}

Or...

{% set count = [] %}
{% for something-that-loops %}
   {% set __ = count.append(1) %} 
   <div> Lorem ipsum meepzip dolor...
   {{ count|length }}
   </div>
{% endfor %}

(From comments by @eyettea and @PYB)

Stabledog
  • 3,110
  • 2
  • 32
  • 43
Peter Hollingsworth
  • 1,870
  • 1
  • 18
  • 18
  • Peter Hollingsworth. Thanks a lot for that info, helped me a lot. – mthecreator Dec 09 '16 at 19:30
  • 7
    Another solution that's a bit cleaner imo is to initialize an empty list `{% set count = [] %}`, add an item to the list in every loop `{% set __ = index.append(1) %}` and use the length to display the index `index|length`. – eyettea Feb 06 '17 at 16:16
  • 1
    Also, you can use [do](http://jinja.pocoo.org/docs/dev/templates/#expression-statement) statement like this `{% do index.append(1) %}` But you have to add extention described [here](http://jinja.pocoo.org/docs/2.9/extensions/#expression-statement) – AstraSerg Jun 10 '17 at 07:12
  • 3
    Correcting @eyettea 's clean solution: Another solution that's a bit cleaner imo is to initialize an empty list `{% set count = [ ] %}`, add an item to the list in every loop `{% set __ = count.append(1) %}` and use the length to display the count `count|length` – PYB Jul 02 '19 at 00:41
20

Here's my solution:

Put all the counters in a dictionary:

{% set counter = {
    'counter1': 0,
    'counter2': 0,
    'etc': 0,
    } %}

Define a macro to increment them easily:

{% macro increment(dct, key, inc=1)%}
    {% if dct.update({key: dct[key] + inc}) %} {% endif %}
{% endmacro %}

Now, whenever you want to increment the 'counter1' counter, just do:

{{ increment(counter, 'counter1') }}
Jahid
  • 21,542
  • 10
  • 90
  • 108
5

if anyone want to add a value inside loop then you can use this its working 100%

{% set ftotal= {'total': 0} %} 
{%- for pe in payment_entry -%}
    {% if ftotal.update({'total': ftotal.total + 5}) %}{% endif %} 
{%- endfor -%}

{{ftotal.total}}

output = 5

pratik soni
  • 51
  • 1
  • 1
2

Came searching for Django's way of doing this and found this post. Maybe someone else need the django solution who come here.

{% for item in item_list %}
    {{ forloop.counter }} {# starting index 1 #}
    {{ forloop.counter0 }} {# starting index 0 #}

    {# do your stuff #}
{% endfor %}

Read more here: https://docs.djangoproject.com/en/1.11/ref/templates/builtins/

Tuxedo Joe
  • 822
  • 6
  • 18
2

I was struggle with this behavior too. I wanted to change div class in jinja based on counter. I was surprised that pythonic way did not work. Following code was reseting my counter on each iteration, so I had only red class.

{% if sloupec3: %}
    {% set counter = 1 %}
    {% for row in sloupec3: %}
        {% if counter == 3 %}
            {% set counter = 1 %}        
        {% endif %} 

        {% if  counter == 1: %}
           <div class="red"> some red div </div>
        {% endif %} 

        {% if counter == 2: %}
           <div class="gray"> some gray div </div>
        {% endif %} 

        {% set counter = counter + 1 %} 

    {% endfor %}

{% endif %}

I used loop.index like this and it works:

{% if sloupec3: %}

    {% for row in sloupec3: %} 

        {% if  loop.index % 2 == 1: %}
           <div class="red"> some red div </div>
        {% endif %} 

        {% if loop.index % 2 == 0: %}
           <div class="gray"> some gray div </div>
        {% endif %}  

    {% endfor %}

{% endif %}
TomRavn
  • 1,134
  • 14
  • 30
0

Just to shed more light into this problem. Jinja2 variables behaves differently from that of conventional scripting languages, you can't modify the variable in a for loop.Hence to bypass this behaviour you can use a dictionary, since you can change the value of the dictionary.

**{% set margin={"margin_value":0} %}** 
{% for lang in language %}
<ul>
    <li style="margin-right: {{ margin.margin_value}}px">{{ lang }}</li>
</ul>
**{% if margin.update({"margin_value":margin.margin_value + 2}) %} 
{% endif %}** 
{% endfor %}

In the above code the value of the dictionary is being modified.

George
  • 71
  • 6