补偿Jinja2循环中赋值的非持久性

时间:2017-10-12 22:29:21

标签: jinja2

根据“范围行为”小节下的Assignments section Jinja文档:

  

请记住,无法在块内设置变量并将其显示在块外。这也适用于循环。该规则的唯一例外是if语句不引入范围。

我理解else循环中for语句的功能,以及loop.index等特殊变量,但这些并不能解决我的问题。

我有一个输出文章的小部件,但前提是它们符合某个标准。为了减少测试用例,这里有一个快速示例代码:

{% set maxiterations = 3 %}
{% set iterations = 0 %}
{% for item in seq %}
  {% if item == "bar" and iterations < maxiterations %}
    {{ item.foo }}
    {% set iterations = iterations + 1 %}
  {% endif %}
{% endfor %}

这当然不起作用:迭代总是等于1loop.index无济于事;我不想计算跳过的迭代次数。我该如何解决这个问题?

2 个答案:

答案 0 :(得分:1)

如果在循环外设置'iterations',则无法在循环内修改它。 您可以通过使用对象而不是“迭代”的标量来打败此行为:

{% set maxiterations = 3 %}
{% set iterations = [0] %}
{% for item in seq %}
  {% if item == "bar" and iterations[0] < maxiterations %}
    {{ item.foo }}
    {% iterations.append(iterations.pop() + 1) %}
  {% endif %}
{% endfor %}

答案 1 :(得分:1)

虽然@SumanKalyan是绝对正确的(当你在循环外设置循环时你不能修改循环内的迭代),我发现他的解决方案,以及Jinja2中可用的do语句似乎都被理解在我的背景下。

尝试@ SumanKalyan的建议或do总是返回错误,如:

CRITICAL: TemplateSyntaxError: Encountered unknown tag 'iterations'.
Jinja was looking for the following tags: 'elif' or 'else' or 'endif'.
The innermost block that needs to be closed is 'if'.

缺少对do的支持,原因是我的具体应用程序(B flag)缺少此扩展程序。我可以简单地指定它,但我选择了不同的路线。我发现了一个不同的答案,很大程度上是因为 Jinja2 for循环支持条件语句。因此:

{% set max_iterations = 3 %}
{% for item in seq if item == "bar" %}
    {% if index.loop < max_iterations %}
        {{ item.foo }}
    {% endif %}
{% endfor %}

这满足了案例的核心部分:如果item.foo,则仅输出item == bar;不要让它成为seq迭代的子集的一部分。

在我的实际实现中(超出此处提供的测试用例),我有多个条件。一个人需要根据外部设置变量的真实性和特定的循环迭代(第一个,当它发生时)跳过一个项目。如何解决这个问题的一个例子:

{% set baz == true %}
{% if baz %}
    {% set max_iterations = 4 %}
{% else %}
    {% set max_iterations = 3 %}
{% endif %}

{% for item in seq if item == "bar" %}
    {% if index.loop < max_iterations and not(baz and loop.index == 1) %}
        {{ item.foo }}
    {% endif %}
{% endfor %}

对于好奇的,实际的代码(也是Pelican):

{# Only show the widget content if:
   - There a configured count of articles to display
   - There's any articles in articles_list
   - There's at least 2 articles...
   - ...Or at least 1 article if the widget count is set to 1 #}

{% if ARTICLES_WIDGET_COUNT and articles_list|length != 0 and (articles_list|length > 1 or ARTICLES_WIDGET_COUNT == 1) %}
    <aside class="siteFooter_articles widget">
        {% if ARTICLES_WIDGET_NAME %}
            <h1 class="widget_title">{{ ARTICLES_WIDGET_NAME }}</h1>
        {% endif %}

        <ol class="imageList list-noType">
            {# If this is the index page, the first article is always displayed.
            It gets skipped inside the loop, so increment the counter #}
            {% if isindex %}
                {% set max_iterations = ARTICLES_WIDGET_COUNT + 1 %}
            {% else %}
                {% set max_iterations = ARTICLES_WIDGET_COUNT %}
            {% endif %}

            {# Skip displaying this article if this is the page for the article being displayed in full
            If this isn't an article page, always display the item (note the additional condition below for index pages) #}
            {% for article in articles_list if article.url != thisarticle.url or thisarticle == null %}
                {# If this is the index page and the first loop iteration, the article to be displayed in the widget would be the same one
                displayed in full on the page. So, skip it. (Note the counter is incremented before the loop to account for this.) #}

                {% if not(loop.index == 1 and isindex) %}
                    {% set articles_widget = true %}
                    {% include 'includes/articleitem.html' %}
                {% endif %}

                {# If the set number of articles have been added to the widget, we're done here. #}
                {% if loop.index == max_iterations %} {% break %} {% endif %}
            {% endfor %}
        </ol>

        <p class="readMore">
            <a class="readMore_link" href="{{ SITEURL }}/{{ ARTICLES_URL }}">More...</a>
        </p>
    </aside>
{% endif %}