Как выбрать/уменьшить список словарей в Flask/Jinja

У меня есть шаблон Jinja со списком словарей. Вопросы для заказа. Я хотел бы уменьшить значения списка или поиска на основе ключей/значений словарей. Вот пример:

{%
    set ordered_dicts = [
        {
            'id': 'foo',
            'name': 'My name is Foo'
        },
        {
            'id': 'bar',
            'name': 'My name is Bar'
        }
    ]
%}

Если у меня есть переменная some_id = 'foo', как мне получить 'My name is Foo' из ordered_dicts в моем шаблоне Jinja?

Я попробовал select() и selectattr(), но не смог понять их на основе документация. Вот что я пробовал:

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

Это выводит:

<generator object _select_or_reject at 0x10748d870>

Я не думаю, что правильно понимаю select() и selectattr().

Нужно ли мне перебирать список и выполнять поиск вручную?


Обновление:

Как указывал Codegeek и Гипи, мне нужно сделать что-то подобное с генератором:

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

Полученная ошибка: TemplateRuntimeError: no test named 'foo', которая разъясняет, как работает selectattr(). Второй аргумент должен быть одним из встроенных тестов. Насколько я могу судить, ни один из этих тестов не позволит мне проверить, соответствует ли значение, связанное с ключом, другим значением. Вот что я хотел бы сделать:

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

Но это не работает, так как тест sameas проверяет, являются ли два объекта действительно одним и тем же объектом в памяти, а не эквивалентны две строки/числа.

Итак, можно ли выбрать элемент на основе теста сравнения ключей/значений?

Ответ 1

Я только что сделал backported equalto следующим образом:

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

После этого этот пример из 2.8 docs работает:

{{ users|selectattr("email", "equalto", "[email protected]") }}

Обновление: Flask имеет декоратор для регистрации тестов, немного более чистый синтаксис: http://flask.pocoo.org/docs/api/#flask.Flask.template_test

Ответ 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") }}

Ответ 3

Для людей, у которых нет selectattr (например, вы застряли с Jinja2.6), и не хотите создавать еще один настраиваемый фильтр, эти 2 строки быстро решают вашу проблему.

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

Ответ 4

Более естественным способом для 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,
        }

Ответ 5

select() и selectattr() действуют на list и возвращают list, поэтому, если вы знаете, что только один результат принимает первый из генератора, i.e

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

Примечание: не проверен код

Ответ 6

Похоже, что в Jinja2.8 идет эквивалентный фильтр (changelog), но у него еще нет даты выпуска (24 февраля 2014 года). В качестве обходного пути я бы предложил использовать фильтр 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>