Jinja: Impossible d'itérer sur l'objet avec l'élément "items"

Créé le 9 janv. 2017  ·  10Commentaires  ·  Source: pallets/jinja

Un serveur renvoie ce JSON :
{
"articles": [1,2,3]
...
}

Une tentative d'itération sur les « éléments » produit :
{% pour el dans data.items %}
....
TypeError : l'objet 'builtin_function_or_method' n'est pas itérable

Ci-dessus se produit parce que Python dict a une fonction intégrée "items()".

En tant que temp. solution de contournement, j'ai installé un crochet et renommer les clés dans la réponse JSON de "items" à "__items", puis les transmettre à Jinja2. À ce stade, j'itére bien l'attribut renommé :
{% pour el dans data.__items %}

Idéalement, Jinja2 devrait ignorer les fonctions intégrées dans la boucle for ou l'instruction if, à moins qu'elle ne soit spécifiquement invoquée avec une notation function().

Commentaire le plus utile

Vous pouvez utiliser {% for el in data['items'] %} pour hiérarchiser l'élément. Ceci est également couvert dans la documentation. http://jinja.pocoo.org/docs/2.9/templates/#variables

Tous les 10 commentaires

Vous pouvez utiliser {% for el in data['items'] %} pour hiérarchiser l'élément. Ceci est également couvert dans la documentation. http://jinja.pocoo.org/docs/2.9/templates/#variables

Il échouera si la réponse JSON n'inclut parfois pas les « éléments » (disons qu'il s'agissait d'un attribut facultatif de la réponse). Dans ce cas, {% for el in data['items'] %} produira également :
TypeError : l'objet 'builtin_function_or_method' n'est pas itérable

...puisque items est toujours défini (en tant que fonction intégrée).

Et c'est un bug, OMI. Les données['items'] ne doivent jamais entrer en collision avec les fonctions intégrées. En d'autres termes, si vous souhaitez itérer proprement un attribut "items" facultatif, vous êtes obligé de le renommer. Idéalement, cela ne devrait pas être le cas.

Pourquoi la réponse JSON n'inclurait-elle pas d'éléments ? Je ne suis pas tout à fait sûr de voir le problème ici. Vous savez sûrement à l'avance quelles sont vos données. Vous pouvez également dans les modèles vérifier les clés avec l'opérateur in sur les dictionnaires déjà.

Changer ce comportement est totalement hors de question car des milliers de modèles seraient cassés si nous supprimions la prise en charge de cet attribut/élément.

Je ne suggère pas de laisser tomber quoi que ce soit, juste pour le rendre plus strict.

Le problème aujourd'hui est que lorsque vous utilisez la notation de recherche explicite : data['items'] il trouve une fonction de dictionnaire intégrée et il ne devrait pas . Python ne le fait pas, pas plus que jinja. Si l'on a besoin d'appeler la fonction intégrée (ou n'importe quelle fonction d'ailleurs), vous avez la notation d'appel pour cela : data.items()

Je ne sais pas s'il serait très clair d'y mettre un cas spécial dict.items . D'autant plus que cela échouerait toujours pour les types de type dict personnalisés ou peut-être même OrderedDict . Je pense qu'il vaut mieux foo['items'] dans ces cas. Ce n'est pas super joli mais un comportement cohérent.

Le seul "correctif/piratage" possible que je puisse imaginer pour gérer cela d'une manière plus agréable est de vérifier si un attribut simple (c'est-à-dire pas un appel de fonction) est utilisé dans une itération et que vous essayez d'itérer sur quelque chose qui n'est pas itérable mais appelable . Dans ce cas, essayer l'élément serait très peu susceptible de casser un code existant. Cependant, la mise en œuvre d'un tel comportement à Jinja serait probablement extrêmement affreuse ;)

Pourquoi est-ce un cas particulier ? Je demande à Jinja de se comporter exactement comme Python. Plus précisément, foo['items'] devrait donner None s'il n'y a pas d'"items" de clé insérés par l'utilisateur. Aujourd'hui, Jinja se contente de trouver la fonction intégrée du même nom. Alors faites simplement ce que fait Python :

>>> print {}.get('items')
None

Btw, je pense également que la notation alternative "foo.items" devrait également être gérée correctement, car vous devez utiliser "()" pour appeler un appelable, mais si vous avez besoin d'une compatibilité descendante pour foo.items soit trouver une touche ou appeler une fonction automatiquement, alors je suppose que c'est dommage.

La logique Jinja pour ['x'] et .x est la suivante :

  • Vérifiez si l'élément/attr (quelle que soit votre syntaxe en Python) existe, si oui, utilisez-le
  • Vérifiez si l'attr/item (le « opposé » de ce que votre syntaxe ferait en Python) existe, si oui, utilisez-le
  • Échec si aucun n'existe

Donc x.items vérifie l'attribut et puisqu'il existe, il l'utilise. Cette logique fonctionne comme ça quel que soit le concours. Améliorer cela nécessiterait que la logique de recherche d'attribut change en fonction de son contexte. Et même alors, cela entraînerait des situations délicates comme ce code qui fonctionnerait :

{% for x in mydict.items %}

vs ce code qui ne fonctionnerait pas

{% set items = mydict.items %}
{% for x in items %}

Et garder un traitement spécial dans ce cas serait tout simplement impossible sans garder une trace de l'origine d'une variable.

@slisznia, vous demandez essentiellement à Jinja2 de changer complètement le système de recherche d'attributs qui est en place depuis environ 7 ans à ce stade.

Ah, je n'ai même pas vu la suggestion de foo['bar'] ne pas faire de recherche d'attribut. C'est sûr que c'est une mauvaise idée ! Quel est le problème avec vous en utilisant simplement for item in data.get('items', []) ?

@ThiefMaster, le mydict.get('items', []) fonctionne bien sûr, et merci d'avoir indiqué une autre méthode que de renommer les clés.

Cette page vous a été utile?
0 / 5 - 0 notes