web-dev-qa-db-fra.com

Comment sélectionner/réduire une liste de dictionnaires dans Flask/Jinja

J'ai un modèle Jinja avec une liste de dictionnaires. L'ordre compte. J'aimerais réduire les valeurs de liste ou de recherche en fonction des clés/valeurs des dictionnaires. Voici un exemple:

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

Si j'ai une variable some_id = 'foo', comment puis-je obtenir 'My name is Foo' sur ordered_dicts dans mon modèle Jinja? 

J'ai essayé select() et selectattr() mais je ne pouvais pas les comprendre à partir de la documentation. Voici ce que j'ai essayé:

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

Cela génère: 

<generator object _select_or_reject at 0x10748d870>

Je ne pense pas comprendre correctement l'utilisation de select() et de selectattr()

Dois-je parcourir la liste et effectuer la recherche manuellement?


Mettre à jour: 

Comme Codegeek et Gipi l'ont souligné, je dois faire quelque chose comme ceci avec le générateur:

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

L'erreur résultante: TemplateRuntimeError: no test named 'foo', qui explique le fonctionnement de selectattr(). Le deuxième argument doit être l'un des les tests intégrés . Autant que je sache, aucun de ces tests ne me permettra de vérifier si la valeur associée à une clé correspond à une autre valeur. Voici ce que j'aimerais faire:

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

Mais cela ne fonctionne pas, car le test sameas vérifie si deux objets sont vraiment le même objet en mémoire et non si deux chaînes/nombres sont équivalents. 

Alors, est-il possible de choisir un article basé sur un test de comparaison clé/valeur?

19
No Surprises

J'ai juste rétroporté equalto comme ceci:

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

Après cela cet exemple de 2.8 docs fonctionne:

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

Update: Flask a un décorateur pour enregistrer les tests, syntaxe légèrement plus propre: http://flask.pocoo.org/docs/api/#flask.Flask.template_test

22
artvolk

Pour les personnes qui n'ont pas selectattr (par exemple, vous êtes coincé avec Jinja2.6) et ne souhaitez pas créer un autre filtre personnalisé, ces 2 lignes vont résoudre votre problème très rapidement.

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

Consultez https://github.com/ansible/ansible/issues/8836 pour ce numéro.

Une solution/solution consiste à créer un fichier filter_plugins/core.py dans le répertoire de votre classeur avec le contenu suivant:

def filter_list(list, key, value):
    return filter(lambda t: t[key] == value, list)

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

Et utilisez-le ainsi:

{{ ordered_dicts|byattr("id", "foo") }}
2
Michael Wyraz

Une manière plus naturelle pour Ansible est de créer /etc/ansible/test_plugins/custom.py avec du contenu

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,
        }
1
Sergey GRIZZLY

select() et selectattr() agissent sur un list et retournent un list, donc si vous savez qu'il n'y a qu'un seul résultat, prenez le premier du générateur, c'est-à-dire

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

Note: code non testé

1
gipi

Il semble que le filtre equalto arrive dans Jinja2.8 ( changelog ) mais il n'a pas encore de date de sortie fixée (24 février 2014). Pour contourner le problème, je vous suggère d'utiliser le filtre 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>
0
lawicko