【问题标题】:Can a Jinja variable's scope extend beyond in an inner block?Jinja 变量的范围可以扩展到内部块之外吗?
【发布时间】:2011-06-19 17:06:12
【问题描述】:

我有以下 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 语句,因此 desired mybool 永远不会设置。

如何设置“全局”mybool 以便在最后一个 if 语句中使用它?

编辑

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

编辑

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

【问题讨论】:

  • 这不能回答您的问题,但您可以将mybool 设置为上下文变量并将其传递到模板中
  • 这个想法很好,但不幸的是它不起作用。只要您在模板中使用“set”,该变量的范围就是本地的。
  • > 解决方案如下。我仍然很好奇为什么上面的建议不起作用。有谁知道他们被弃用了?它们被删除是因为在生成的代码中无法正确预测它们在 Python 堆栈上的冒泡程度。有一些黑客是可能的,但不值得付出努力。将逻辑排除在模板之外:)

标签: templates variables scope jinja2


【解决方案1】:

解决此限制的一种方法是启用"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",])

【讨论】:

  • 不需要这个“do”表达式语句。将表达式放在 if 条件中 :) {% if exists.append(1) %}{% endif %}
  • 我需要实际修改全局变量并最终得到数组:{% set platform = [] %} 要读取“全局”变量:{% platform[-1] %} 要“更改" "全局变量:{% if platform.append(new_platform) %}{% endif %}
  • @schettino72 当您没有do 表达式时,甚至还有更短的方法可以做到这一点:{% set _ = exists.append(1) %}。这是DebOps 项目(Ansible 的东西)中的常见做法。
【解决方案2】:

回答一个相关问题:我想要一个全局计数器来记录我在模板中输入某个 if 块的次数,结果如下。

模板顶部:

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

在我要数的 if 块中:

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

显示计数时:

{{ counter|length }}

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

【讨论】:

    【解决方案3】:

    2018 年更新

    截至 Jinja 2.10(2017 年 11 月 8 日)有一个 namespace() 对象来解决这个特殊问题。更多详情和示例见官方Assignments documentationclass documentation 然后说明了如何为命名空间分配多个值。

    【讨论】:

    • 我发布了一个usnig namespace()对象here的模板。
    • 感谢您的回答-如果它包含一个简单的示例,我会赞成
    • @user2682863 参考文档包括示例。
    • @jens 是的,但是一个简单的例子会让这个答案更好,并确保它在链接失效时仍然有用
    【解决方案4】:

    您可以使用此 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'}}])
    

    【讨论】:

      【解决方案5】:

      以下是任何想要使用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) #}
      

      【讨论】:

        【解决方案6】:

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

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

        如果要修改上下文,请编写一个返回变量的函数,而不是使用 set 将其分配给变量:

        {% set comments = get_latest_comments() %}

        Source

        【讨论】:

          【解决方案7】:

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

          由于 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] %}
          

          希望这可以帮助其他人尝试找出相同的方法。

          【讨论】:

            【解决方案8】:

            发现这个很棒的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 操作的结果(注意自己)。

            【讨论】:

              猜你喜欢
              • 2013-01-25
              • 1970-01-01
              • 2021-08-18
              • 1970-01-01
              • 2022-06-12
              • 1970-01-01
              • 2016-12-20
              • 1970-01-01
              • 1970-01-01
              相关资源
              最近更新 更多