【问题标题】:How to select/reduce a list of dictionaries in Flask/Jinja如何在 Flask/Jinja 中选择/减少字典列表
【发布时间】:2013-12-30 00:31:41
【问题描述】:

我有一个带有字典列表的 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() 的工作原理。第二个参数必须是the builtin tests 之一。据我所知,这些测试都不会让我检查与键关联的值是否与另一个值匹配。这是我想做的:

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

但这不起作用,因为sameas 测试检查两个对象是否真的是内存中的同一个对象,而不是两个字符串/数字是否相等。

那么是否可以根据键/值比较测试来选择项目?

【问题讨论】:

  • 你需要进一步迭代它,因为你得到了一个生成器对象。
  • 应该等于,但我自己似乎无法让这个工作

标签: python flask jinja2


【解决方案1】:

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

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

注意:代码未经测试

【讨论】:

  • 这有帮助,但还不够。更新我的问题。
  • selectattr 返回一个生成器,您可能希望将其转换为列表{{ oredered_dicts|selectattr("id", "foo")|list|count }}
【解决方案2】:

对于没有 selectattr 的人(例如,您被 Jinja2.6 卡住了),并且不想再制作另一个自定义过滤器,这两行将真正快速解决您的问题。

{% set selection = [] %}
{% for x in biglist if x.criteria == 'pickme' %}{% do selection.append(x) %}{% endfor %}

【讨论】:

  • "AnsibleError: 模板化字符串时出现模板错误:遇到未知标签 'do'
  • @jmcollin92 代替 {% do selection.append(x) %} 使用 {{ selection.append(x) }}。它应该可以工作。
【解决方案3】:

Ansible 更自然的方法是使用内容创建 /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,
        }

【讨论】:

    【解决方案4】:

    请查看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") }}
    

    【讨论】:

      【解决方案5】:

      我刚刚像这样向后移植了equalto

      app.jinja_env.tests['equalto'] = lambda value, other : value == other
      

      之后this example from 2.8 docs 工作:

      {{ users|selectattr("email", "equalto", "foo@bar.invalid") }}
      

      更新:Flask 有一个用于注册测试的装饰器,语法更简洁:http://flask.pocoo.org/docs/api/#flask.Flask.template_test

      【讨论】:

      • 再次谷歌搜索并找到了我自己的答案。 :)
      • 对我来说也是一样的。你把这段代码 app.jinja_env 放在哪里。 ... ?您的示例中的 app 是什么?你能过去一段真正完整的代码吗?提前谢谢。
      • equalto 是默认的 jinja 测试 jinja.palletsprojects.com/en/2.11.x/templates/#eq
      【解决方案6】:

      看起来 equalto 过滤器即将在 Jinja2.8 (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>
      

      【讨论】:

        猜你喜欢
        • 2021-03-03
        • 2014-05-16
        • 2017-07-16
        • 1970-01-01
        • 2019-12-03
        • 1970-01-01
        • 2014-10-11
        • 1970-01-01
        • 2016-01-02
        相关资源
        最近更新 更多