Jinja循环嵌套列表时生成空输出

5 投票
3 回答
7068 浏览
提问于 2025-04-18 14:00

我有一个这样的结构列表,传递给模板,名字叫 bars,是在 Python 3.4 中的:

[{'var': 1.18, 'occurrences': [0.0805, 0.0808, 0.0991, 0.0994, 0.2356], 'name': 'item name'},
 {'var': 2.31, 'occurrences': [1.0859, 1.1121, 1.4826, 1.4829, 1.8126, 1.8791], 'name': 'other name'}]

我想为每个字典生成以下输出:

% List with names
item 1: item name
item 2: other name

% List with vars
item 1: 1.18
item 2: 2.31

% List with occurences
item 1: 0.0805, 0.0808, 0.0991, 0.0994, 0.2356
item 2: 1.0859, 1.1121, 1.4826, 1.4829, 1.8126, 1.8791

前两个输出没问题,但我无法循环处理出现次数列表。我使用了以下的 jinja 模板:

{% for item in bars %}
  item {{ loop.index }}: {{ item.name }}
{% endfor %}

{% for item in bars %}
  item {{ loop.index }}: {{ item.var }}
{% endfor %}

{% for item in bars recursive %}
  {% if item.occurrences %}
    Item {{ loop.index}}: {{ loop(item.occurrences) }}
  {% else %}
    No content
  {% endif %}
{% endfor %}

这在第三种情况下产生了奇怪的输出:

Item 1:       No content
No content
No content
No content
No content

Item 2:       No content
No content
No content
No content
No content
No content

这很奇怪,因为它似乎在循环列表中的每个项目和出现次数,但却没有通过内容的测试。我哪里做错了?

编辑:所有三个回答都给了我正确的方向,但 @famousgarkin 给出的答案最详细、最灵活。最后我得到了以下解决方案:

{% for item in bars %} 
  Item {{ loop.index }}: {% for occurrence in item.occurrences %} subitem {{ loop.index }}: {{ occurrence }} {% endfor %}
{% endfor %}

这让我可以将每个项目放在不同的上下文中,这正是我想要的。但由于这个目标一开始并不明确,我希望能给你们所有的答案点赞。抱歉,但还是感谢你们的快速帮助!

3 个回答

0

这里是可以正常运行的代码。

  • 管理空白字符,以保持行的格式如预期
  • 使用过滤器 join 来创建出现次数的列表
  • 在遍历出现次数时去掉了 recursive(因为数据是平坦的,不是嵌套的)

运行这个脚本:

from jinja2 import Template

bars = [{'var': 1.18, 'occurrences': [0.0805, 0.0808, 0.0991, 0.0994, 0.2356], 'name': 'item name'},
     {'var': 2.31, 'occurrences': [1.0859, 1.1121, 1.4826, 1.4829, 1.8126, 1.8791], 'name': 'other name'}]
templ_str = """
% List with names
{% for item in bars -%}
    item {{ loop.index }}: {{ item.name }}
{% endfor %}
% List with vars
{% for item in bars -%}
    item {{ loop.index }}: {{ item.var }}
{% endfor %}
% List with occurrences
{%- for item in bars %}
item {{ loop.index}}:{{" "}}
    {%- if item.occurrences -%}
        {{ item.occurrences|join(", ") }}
    {%- else -%}
    No content
    {%- endif -%}
{%- endfor -%}
"""
templ = Template(templ_str)
print templ.render(bars=bars)

你将得到以下输出:

% List with names
item 1: item name
item 2: other name

% List with vars
item 1: 1.18
item 2: 2.31

% List with occurrences
item 1: 0.0805, 0.0808, 0.0991, 0.0994, 0.2356
item 2: 1.0859, 1.1121, 1.4826, 1.4829, 1.8126, 1.8791
0

只需要把列表打印出来。

{% for item in bars %} #recursive not needed
  {% if item.occurrences %}
    Item {{ loop.index }}: {{ item.occurrences }} #we're not looping here, just printing out the list
  {% endif %}
{% endfor %}

如果 bars 里总是有 occurrences,那么你甚至不需要检查 if

要打印 occurrences 中的每个项目,只需逐个遍历它们,

Item {{ loop.index}}: {% for occurrence in occurrences %}
                       {{ occurrence }}{% if not loop.last %}, {% endif %}
                      {% endfor %}

如果你反正要打印整个列表,那你可以用一个过滤器,直接返回列表的字符串表示,不带方括号。

3

如果你不想用完全相同的逻辑来格式化嵌套的项目,那就不要使用递归。

在你的例子中,当循环处理 bars 项目时,它走到了正确的路径,因为你可以在输出中看到 Item n:。然后它会进行递归调用来处理 item.occurrences 项目。现在,当它询问 occurrences 是否存在时,其实是在询问 bar.occurrences[i].occurrences[j],而这个当然没有 occurrences 属性,所以它就走上了 No content 的路径。你可以通过以下代码看到这个过程:

{% for item in bars recursive %}
  {% if item.occurrences %}
    Item {{ loop.index }}: {{ loop(item.occurrences) }}
  {% else %}
    No content, {{ item.__class__ }}, {{ item }}
  {% endif %}
{% endfor %}

输出结果是:

Item 1: No content, <type 'float'>, 0.0805
No content, <type 'float'>, 0.0808
No content, <type 'float'>, 0.0991
No content, <type 'float'>, 0.0994
No content, <type 'float'>, 0.2356
...

比如,它会这样工作:

{% for item in bars %}
  {% if item.occurrences %}
    Item {{ loop.index }}: {{ item.occurrences }}
  {% else %}
    No content
  {% endif %}
{% endfor %}

输出结果是:

Item 1: [0.0805, 0.0808, 0.0991, 0.0994, 0.2356]
Item 2: [1.0859, 1.1121, 1.4826, 1.4829, 1.8126, 1.8791]

如果你想自己遍历这些项目来提供自己的格式化方式,可以使用 Jinja 的 join 过滤器:

{% for item in bars %}
  {% if item.occurrences %}
    Item {{ loop.index }}: {{ item.occurrences|join(', ') }}
  {% else %}
    No content
  {% endif %}
{% endfor %}

输出结果是:

Item 1: 0.0805, 0.0808, 0.0991, 0.0994, 0.2356
Item 2: 1.0859, 1.1121, 1.4826, 1.4829, 1.8126, 1.8791

或者再循环一次,完全自定义格式化:

{% for item in bars %}
  {% if item.occurrences %}
    Item {{ loop.index }}:
    {% for occurrence in item.occurrences %}
      {{ loop.index }}. {{ occurrence }}
    {% endfor %}
  {% else %}
    No content
  {% endif %}
{% endfor %}

输出结果是:

Item 1:
1. 0.0805
2. 0.0808
3. 0.0991
4. 0.0994
5. 0.2356
...

撰写回答