Jinja变量的范围是否可以扩展到内部块?

时间:2011-02-02 02:57:23

标签: templates variables scope jinja2

我有以下Jinja模板:

{% set mybool = False %}
{% for thing in things %}
    <div class='indent1'>
        <ul>
            {% if current_user %}
              {% if current_user.username == thing['created_by']['username'] %}
                {% set mybool = True %}
                <li>mybool: {{ mybool }}</li> <!-- prints True -->
                <li><a href='#'>Edit</a></li>
              {% endif %}
            {% endif %}
            <li>Flag</li>
        </ul>
    </div>
    <hr />
{% endfor %}

{% if not mybool %}
    <!-- always prints this -->
    <p>mybool is false!</p>
{% else %}
  <p>mybool is true!</p>
{% endif %}

如果for循环中符合条件,我想将mybool更改为true,以便我可以在下面显示mybool is true!。但是,看起来内部mybool的范围仅限于if语句,因此永远不会设置所需的 mybool

如何设置“全局”mybool,以便我可以在上一个if语句中使用它?

修改

我找到some suggestions(只有正确的缓存页面浏览量),但它们似乎不起作用。也许他们在Jinja2中被弃用了......

修改

下面提供的解决方案。我仍然很好奇为什么上述建议不起作用。有谁知道他们被弃用了吗?

8 个答案:

答案 0 :(得分:46)

解决此限制的一种方法是启用"do" expression-statement extension并使用数组而不是布尔值:

{% set exists = [] %}
{% for i in range(5) %}
      {% if True %}
          {% do exists.append(1) %}
      {% endif %}
{% endfor %}
{% if exists %}
    <!-- exists is true -->
{% endif %}

启用Jinja的“do”表达式语句扩展名:e = jinja2.Environment(extensions=["jinja2.ext.do",])

答案 1 :(得分:14)

回答相关问题:我希望得到一个全局计数器,其中包含我在模板中输入某个if-block的次数,最后是以下结果。

位于模板顶部:

{% set counter = ['1'] %}

在if-block中我想算一下:

{% if counter.append('1') %}{% endif %}

显示计数时:

{{ counter|length }}

我相信字符串'1'可以替换为任何字符串或数字。它仍然是一个黑客,但不是一个非常大的。

答案 2 :(得分:8)

你可以使用这个hack(没有扩展名)来解决你的问题:

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'}}])

答案 3 :(得分:5)

更新2018年

自Jinja 2.10(2017年11月8日)起,有一个namespace()对象来解决这个特殊问题。有关详细信息和示例,请参阅官方Assignments documentation;然后class documentation说明了如何为命名空间分配多个值。

答案 4 :(得分:4)

在编写contextfunction()或类似内容时,您可能已经注意到上下文试图阻止您修改它。

如果您已设法使用内部上下文API修改上下文,您可能已经注意到上下文中的更改似乎在模板中不可见。原因是出于性能原因,Jinja仅将上下文用作模板变量的主要数据源。

如果要修改上下文,请编写一个返回变量的函数,而不是可以使用set赋值给变量:

{% set comments = get_latest_comments() %}

Source

答案 5 :(得分:3)

是否需要从列表中找到对象(对象)中的最大条目数(objects_from_db),

由于jinja2和变量范围中已知的原因,这不起作用。

 {% set maxlength = 0 %}
 {% for object in objects_from_db %}
     {% set ilen = object.entries | length %}
     {% if maxlength < ilen %}
         {% set maxlength = ilen %}
     {% endif %}
 {% endfor %}

以下是有效的:

 {% set mlength = [0]%}
 {% for object in objects_from_db %}
     {% set ilen = object.entries | length %}
     {% if mlength[0] < ilen %}
         {% set _ = mlength.pop() %}
         {% set _ = mlength.append(ilen)%}
     {% endif %}
 {% endfor %}
 {% set maxlength = mlength[0] %}

希望这有助于其他人试图找出相同的东西。

答案 6 :(得分:0)

发现了一个很棒的article,它描述了一些小技巧。不可能在其他范围内更改jinja变量的值,但可以修改全局字典值:

# 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}}

我还发现这种语法在不实际使用set的情况下设置值非常有用:

{%-   if foundUser.update({'flag':True}) %}{%- endif %} 

它实际上检查字典上update操作的结果(自我注释)。

答案 7 :(得分:0)

这是所有想要使用namespace()对象以使变量在for循环之外保留的人的普遍情况。

{% 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) #}