Why does using set in a jinja template not persistently change the value from the perspective of later content?

Advertisements

I’m trying to use the jinja2 moudle… It’s working differently than I expected.

The value of the variable has_swimming should change within the for loop, but it resets to the initial value.

The code below is intended to perform a function that finds instances where the hobby is SWIMMING in a list of dictionaries, and wraps them with the desired String characters above and below.

from jinja2 import Environment

if __name__ == '__main__':
    template = \
        '''
{% set has_swimming = 0 %}

{% for row in Class['Students'] %}
{% set name = row['name'] -%}
{% set hobby = row['hobby'] %}
    {% if hobby == 'SWIMMING' and has_swimming == 0 %}
        {% set has_swimming = 1 %}
find swim=============
    {% endif %}
name is {{name}}, hobby is {{hobby}}
    {% if hobby != 'SWIMMING' and has_swimming == 1 %}
        {% set has_swimming = 0 %}
end===============
    {% endif %}
{% endfor %}

{% if has_swimming == 1%}
end===============
{% endif %}
        '''

    env = Environment(extensions=['jinja2.ext.i18n', 'jinja2.ext.do', 'jinja2.ext.loopcontrols'],
                      trim_blocks=True, lstrip_blocks=True)
    new_template = env.from_string(template)
    test_table = dict()
    test_table['Class'] = dict()
    test_table['Class']['Students'] = list()
    test_table['Class']['Students'].append({'name': 'CHOI', 'hobby': 'RUNNINg'})
    test_table['Class']['Students'].append({'name': 'KIM', 'hobby': 'RUNNINg'})
    test_table['Class']['Students'].append({'name': 'JEON', 'hobby': 'SWIMMING'})
    test_table['Class']['Students'].append({'name': 'YOON', 'hobby': 'SWIMMING'})
    write_text = new_template.render(test_table)
    print(write_text)

I have a question about the initialization of the has_swimming variable in the code above.

The code {% set has_swimming = 0 %} initializes the variable to 0 before entering the for loop.

Then, if the hobby of the current row is ‘swimming’,

{% if hobby == 'SWIMMING' and has_swimming == 0 %}
        {% set has_swimming = 1 %}

Current output result:

name is CHOI, hobby is RUNNINg
name is KIM, hobby is RUNNINg
find swim=============
name is JEON, hobby is SWIMMING
find swim=============
name is YOON, hobby is SWIMMING

Expected output result:

name is CHOI, hobby is RUNNINg
name is KIM, hobby is RUNNINg
find swim=============
name is JEON, hobby is SWIMMING
name is YOON, hobby is SWIMMING
end===============

I can’t understand, the has_swimming value should be set ‘1’, but on next row, value of has_swimming resets to zero…

>Solution :

The end=============== is never printed because the assignment {% set has_swimming = 1 %} is limited to inside the surrounding loop block. Per the documentation for the for block from the Jinja2 docs:

Please note that assignments in loops will be cleared at the end of the iteration and cannot outlive the loop scope.

This behavior is also further discussed under Assignments:

Please keep in mind that it is not possible to set variables inside a block and have them show up outside of it. This also applies to loops.

As a workaround (in Jina 2.10+), you can use a namespace object to create assignments that propagate to outer scopes:

{% set ns = namespace(has_swimming=false) %}
{% for row in Class['Students'] %}
    {# ... #}

    {% if hobby == 'SWIMMING' and not ns.has_swimming %}
        {% set ns.has_swimming = true %}
    {% endif %}

    {# ... #}

{% endfor %}

{% if ns.has_swimming %}
    {# ... #}
{% endif %}

Leave a ReplyCancel reply