I have the following Jinja template:
{% set mybool = False %}
{% for thing in things %}
{%
You can solve your problem using this hack (without extensions):
import jinja2
env = jinja2.Environment()
print env.from_string("""
{% set mybool = [False] %}
{% for thing in things %}
<div class='indent1'>
<ul>
{% if current_user %}
{% if current_user.username == thing['created_by']['username'] %}
{% set _ = mybool.append(not mybool.pop()) %}
<li>mybool: {{ mybool[0] }}</li> <!-- prints True -->
<li><a href='#'>Edit</a></li>
{% endif %}
{% endif %}
<li>Flag</li>
</ul>
</div>
<hr />
{% endfor %}
{% if not mybool[0] %}
<!-- always prints this -->
<p>mybool is false!</p>
{% else %}
<p>mybool is true!</p>
{% endif %}
""").render(current_user={'username':'me'},things=[{'created_by':{'username':'me'}},{'created_by':{'username':'you'}}])
One way around this limitation is to enable the "do" expression-statement extension and use an array instead of boolean:
{% set exists = [] %}
{% for i in range(5) %}
{% if True %}
{% do exists.append(1) %}
{% endif %}
{% endfor %}
{% if exists %}
<!-- exists is true -->
{% endif %}
To enable Jinja's "do" expression-statement extension: e = jinja2.Environment(extensions=["jinja2.ext.do",])
Answer to a related question: I wanted to have a global counter of the number of times I entered a certain if-block in the template, and ended up with the below.
At the top of the template:
{% set counter = ['1'] %}
In the if-block I want to count:
{% if counter.append('1') %}{% endif %}
When displaying the count:
{{ counter|length }}
The string '1'
can be replaced with any string or digit, I believe. It is still a hack, but not a very large one.
Found this great article that describes a little hack. It's not possible to change value of a jinja variable in a different scope, but it's possible to modify a global dictionary values:
# works because dictionary pointer cannot change, but entries can
{% set users = ['alice','bob','eve'] %}
{% set foundUser = { 'flag': False } %}
initial-check-on-global-foundUser:
cmd.run:
name: echo initial foundUser = {{foundUser.flag}}
{% for user in users %}
{%- if user == "bob" %}
{%- if foundUser.update({'flag':True}) %}{%- endif %}
{%- endif %}
echo-for-{{user}}:
cmd.run:
name: echo my name is {{user}}, has bob been found? {{foundUser.flag}}
{% endfor %}
final-check-on-global-foundUser:
cmd.run:
name: echo final foundUser = {{foundUser.flag}}
I've also found very helpful this syntax to set the value without actually using set
:
{%- if foundUser.update({'flag':True}) %}{%- endif %}
It actually checks the result of an update
operation on a dictionary (note to self).
Here's the general case for anyone wanting to use the namespace()
object to have a variable persist outside of a for
loop.
{% set accumulator = namespace(total=0) %}
{% for i in range(0,3) %}
{% set accumulator.total = i + accumulator.total %}
{{accumulator.total}}
{% endfor %}` {# 0 1 3 #}
{{accumulator.total}} {# 3 (accumulator.total persisted past the end of the loop) #}
Update 2018
As of Jinja 2.10 (8th Nov 2017) there is a namespace()
object to address this particular problem. See the official Assignments documentation for more details and an example; the class documentation then illustrates how to assign several values to a namespace.