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?
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
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 %}
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") }}
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,
}
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é
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>