在循环内更改变量的值。

113

我想在循环内部更改在循环外声明的变量的值。但是,即使在循环内部更改了它,它仍然保持循环外的初始值。

{% set foo = False %}

{% for item in items %}
  {% set foo = True %}
  {% if foo %} Ok(1)! {% endif %}
{% endfor %}

{% if foo %} Ok(2)! {% endif %}

这将呈现为:

Ok(1)!

所以到目前为止,我找到的唯一(不好的)解决方案是这个:

{% set foo = [] %}

{% for item in items %}
  {% if foo.append(True) %} {% endif %}
  {% if foo %} Ok(1)! {% endif %}
{% endfor %}

{% if foo %} Ok(2)! {% endif %}

这将呈现为:

Ok(1)!
Ok(2)!

但是,这很丑陋!有没有其他更优雅的解决方案?


5
我认为没有其他办法。也许你可以重构代码,以便不需要设置变量。 - Alex Morega
3
+1 对于这个问题,因为它成为了我的答案 :) - Boris Zagoruiko
1
@Shankar Cabus:好问题。这可能应该归类为“Jinja烦恼”。 - dreftymac
1
我认为这个问题在以下两个链接中已经有了重复的回答: https://dev59.com/rGsz5IYBdhLWcg3w9s2r 和 https://dev59.com/42445IYBdhLWcg3wappH#4880398 (我刚开始,无法标记该问题)您可以使用Pashka的方法,并添加jinja2.ext.do来使其更加简洁。 - Gerardo Roza
我发现这段代码是解决构造问题的唯一方法,因为我无法在salt+jinja中使用:somelist|map(format)|join - Martin
3个回答

97

也可以尝试基于词典的方法。这种方法看起来会更清晰易懂。

{% set vars = {'foo': False} %}

{% for item in items %}
  {% if vars.update({'foo': True}) %} {% endif %}
  {% if vars.foo %} Ok(1)! {% endif %}
{% endfor %}

{% if vars.foo %} Ok(2)! {% endif %}

这也将呈现:

Ok(1)!
Ok(2)!

13
还是很丑,但它能用。我很惊讶地发现没有一种 Pythonic 的方法可以在 jinja2 中实现这个。 - kramer65
2
如果你需要多个变量,这样做肯定会更加简洁。 - Ade Miller
1
简而言之,set vars 在 for 循环中根本不起作用吗? - ThorSummoner
3
解决方案似乎正在进行中: https://github.com/pallets/jinja/pull/684; https://github.com/pallets/jinja/pull/676 - Michael Scheper
1
@ThorSummoner 看起来是这样。我听到很多人赞扬 Python。他们也只是在用最基本的东西。 - Toskan
显示剩余3条评论

87

文档所述:

请注意,循环中的赋值将在迭代结束时被清除,并且不能超出循环范围。

但从2.10版本开始,您可以使用命名空间

{% set ns = namespace(foo=false) %}      
{% for item in items %}
  {% set ns.foo = True %}
  {% if ns.foo %} Ok(1)! {% endif %}
{% endfor %}

{% if ns.foo %} Ok(2)! {% endif %}

4
当你写 namespace(foo=false) 时,其中的小写字母 f 是否是 jinja2 的俚语,还是你的意思是使用 Python 中 boolean 类型所要求的大写字母 F False?请翻译以上内容。 答:您提到的 namespace(foo=false) 中的小写字母 f 是否为 jinja2 的行话,还是您的意思是使用 Python 中布尔类型所需的大写字母 F False? - mas
8
小写的 false 是 Jinja 约定的一部分:“特殊常量 true、false 和 none 的确是小写的。由于这在过去引起了混淆(True 曾经扩展为一个未定义的变量,被视为 false),现在三者也可以用标题大小写来编写(True、False 和 None)。但是,为了保持一致性(所有 Jinja 标识符都是小写的),你应该使用小写版本. - Omer
1
经过几个小时的尝试,我终于找到了这个答案!这在Home Assistant模板中有效。 - marlar
来源:https://github.com/pallets/jinja/pull/684 - Toledo
来源:https://github.com/pallets/jinja/pull/684 - Toledo

0
你可以这样做来清理模板代码。
{% for item in items %}
  {{ set_foo_is_true(local_vars) }}
  {% if local_vars.foo %} Ok(1)! {% endif %}
{% endfor %}
{% if local_vars.foo %} Ok(2)! {% endif %}

在服务器代码中使用

items = ['item1', 'item2', 'item3']
#---------------------------------------------
local_vars = { 'foo': False }
def set_foo_is_true(local_vars):
  local_vars['foo'] = True
  return ''
env.globals['set_foo_is_true'] = set_foo_is_true    
#---------------------------------------------
return env.get_template('template.html').render(items=items, local_vars=local_vars)

这可以推广到以下情况

{{ set_local_var(local_vars, "foo", False) }}
{% for item in items %}
  {{ set_local_var(local_vars, "foo", True) }}
  {% if local_vars.foo %} Ok(1)! {% endif %}
{% endfor %}
{% if local_vars.foo %} Ok(2)! {% endif %}

在服务器代码中使用

items = ['item1', 'item2', 'item3']
#---------------------------------------------
local_vars = { 'foo': False }
def set_local_var(local_vars, name, value):
  local_vars[name] = value
  return ''
env.globals['set_local_var'] = set_local_var
#---------------------------------------------
return env.get_template('template.html').render(items=items, local_vars=local_vars)

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接