如何在Flask/Jinja中选择/减少一个字典列表

22

我有一个包含字典列表的Jinja模板。顺序很重要。我想根据字典中的键/值来减少列表或查找值。以下是一个示例:

{%
    set ordered_dicts = [
        {
            'id': 'foo',
            'name': 'My name is Foo'
        },
        {
            'id': 'bar',
            'name': 'My name is Bar'
        }
    ]
%}
如果我有一个变量some_id = 'foo',我该如何从我的Jinja模板的ordered_dicts中获取'My name is Foo'?我尝试了select()selectattr(),但根据文档无法弄清楚它们的用法。以下是我的尝试:
{{ ordered_dicts|selectattr("id", "foo") }}

那将输出:

<generator object _select_or_reject at 0x10748d870>

我觉得我没有正确理解select()selectattr()的使用方法。

我需要遍历列表并手动查找吗?


更新:

正如codegeek和gipi指出的那样,我需要像这样对生成器进行处理:

{{ ordered_dicts|selectattr("id", "foo")|list }}

出现的错误:TemplateRuntimeError: no test named 'foo',这表明了selectattr()如何工作。第二个参数必须是内置测试之一。据我所知,这些测试都不能让我检查与键相关联的值是否与另一个值匹配。以下是我想做的:

{{ ordered_dicts|selectattr("id", "sameas", "foo")|list }}

但是这并不起作用,因为“sameas”测试检查的是两个对象是否真正相同,而不是两个字符串/数字是否等效。

那么,是否有可能根据键/值比较测试选择一个项目?


你需要进一步迭代它,因为你得到了一个生成器对象。 - codegeek
应该是相等的,但是我自己似乎无法让它正常工作。 - njzk2
6个回答

24

14
再次搜索后,我找到了自己的答案。 :) - artvolk
我也遇到了同样的问题。你在哪里放置app.jinja_env这段代码呢?在你的例子中,app是什么?你能贴上一整段真实完整的代码吗?先谢谢了。 - jmcollin92
"equalto" 是 Jinja 的默认测试 https://jinja.palletsprojects.com/en/2.11.x/templates/#eq - botchniaque

7
对于没有 selectattr(例如您被困在 Jinja2.6 中)且不想创建另一个自定义过滤器的人,这两行代码将快速解决您的问题。
{% set selection = [] %}
{% for x in biglist if x.criteria == 'pickme' %}{% do selection.append(x) %}{% endfor %}

1
Ansible错误:模板化字符串时出现模板错误:遇到未知标签“do”。 - jmcollin92
1
@jmcollin92,你需要使用{{ selection.append(x) }}来代替{% do selection.append(x) %}。这样应该会起作用。 - pajus_cz

3

select()selectattr() 作用于一个 list 并返回一个 list,因此如果你知道只有一个结果,请从生成器中获取第一个结果,即

{{ oredered_dicts|selectattr("id", "foo")|first }}

注意: 代码未经测试


那有所帮助,但还不够。我正在更新我的问题。 - No Surprises
1
selectattr 返回一个生成器,你可能想将它转换为列表 {{ oredered_dicts|selectattr("id", "foo")|list|count }} - Heretron

2
请看这个问题的解决方法 https://github.com/ansible/ansible/issues/8836
解决办法/变通方法是在你的工作簿目录中创建一个名为filter_plugins/core.py的文件,并加入以下内容:
def filter_list(list, key, value):
    return filter(lambda t: t[key] == value, list)

class FilterModule(object):
    def filters(self):
        return {
            'byattr': filter_list
        }

并这样使用:

{{ ordered_dicts|byattr("id", "foo") }}

1
更自然的方式是创建 /etc/ansible/test_plugins/custom.py 文件,并填写内容。
from __future__ import (absolute_import, division, print_function)
__metaclass__ = type

from ansible import errors

def equalto(value, other):
    return bool(value == other)

class TestModule(object):
    ''' Ansible file jinja2 tests '''

    def tests(self):
        return {
            'equalto' : equalto,
        }

0

看起来 Jinja2.8 将会加入 equalto 过滤器 (changelog),但目前还没有确定发布日期 (2014年2月24日)。作为解决方法,我建议使用 groupby 过滤器:

<ul>
{% for group in persons|groupby('gender') %}
    <li>{{ group.grouper }}<ul>
    {% for person in group.list %}
        <li>{{ person.first_name }} {{ person.last_name }}</li>
    {% endfor %}</ul></li>
{% endfor %}
</ul>

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