Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Setting variable in Jinja for loop doesn't persist between iterations

Tags:

python

jinja2

I want to loop over a list of objects and count how many objects meet a requirement. I based my code off other examples I'd found, but it doesn't work, the count is always 0 after the loop.

For each house, I want to loop over each room and count how many rooms have a bed. I want to output that then reset the count for the next house.

{% for house in city %}
{% set count = 0 %}
    <div>{{ house.address }} has {{ count }} beds in it rooms.</div>
    {% for room in house %}
    {% if room.has_bed == True %}{% set count = count + 1 %}{% endif %}
   {% endfor %}
{% endfor %}
like image 270
aharding378 Avatar asked Oct 25 '17 18:10

aharding378


1 Answers

Jinja 2.10 introduces the namespace object to handle assignment and comparison in loops.

{% set ns = namespace(beds=0) %}
{% for room in house %}
    {% if room.has_bed %}
        {% set ns.beds = ns.beds + 1 %}
    {% endif %}
{% endfor %}
{{ house.address }} has {{ ns.beds }} beds.

Normally, set does not handle attributes, which is why the old answers mutate objects with methods instead. namespace has been special cased so setting attributes does work.

The reason the original counter didn't work is because of Jinja's scope rules. Unlike Python, most blocks are new scopes. set always defines a local variable, except in this new special case of namespace.attribute.


In this specific case, you can accomplish what you want with filters.

{% set beds = house.rooms|selectattr('has_bed')|length %}
{{ house.address }} has {{ beds }} beds.

However, there are cases where storing information across scopes is needed. For example, if you wanted to output some information in addition to incrementing the counter, it would make sense to use a namespace.

like image 84
davidism Avatar answered Sep 25 '22 12:09

davidism