Jinja: Es ist unmöglich, mit dem Element "Items" über ein Objekt zu iterieren

Erstellt am 9. Jan. 2017  ·  10Kommentare  ·  Quelle: pallets/jinja

Ein Server gibt diesen JSON zurück:
{
"Gegenstände": [1,2,3]
...
}

Ein Versuch, über 'items' zu iterieren, führt zu folgendem Ergebnis:
{% für el in data.items %}
....
TypeError: 'builtin_function_or_method'-Objekt ist nicht iterierbar

Oben geschieht, weil Python dict eine eingebaute Funktion "items()" hat.

Als Temp. Um das Problem zu umgehen, habe ich einen Hook installiert und die Schlüssel in der JSON-Antwort von "items" in "__items" umbenannt und dann an Jinja2 übergeben. An diesem Punkt iteriere ich über das umbenannte Attribut fine:
{% für el in data.__items %}

Idealerweise sollte Jinja2 eingebaute Funktionen in einer for-Schleife oder einer if-Anweisung ignorieren, es sei denn, sie wird speziell mit einer function()-Notation aufgerufen.

Hilfreichster Kommentar

Sie können {% for el in data['items'] %} , um das Element zu priorisieren. Dies wird auch in der Dokumentation behandelt. http://jinja.pocoo.org/docs/2.9/templates/#variables

Alle 10 Kommentare

Sie können {% for el in data['items'] %} , um das Element zu priorisieren. Dies wird auch in der Dokumentation behandelt. http://jinja.pocoo.org/docs/2.9/templates/#variables

Es schlägt fehl, wenn die JSON-Antwort gelegentlich die 'items' nicht enthält (sagen wir, es war ein optionales Attribut der Antwort). In diesem Fall erzeugt {% for el in data['items'] %} auch:
TypeError: 'builtin_function_or_method'-Objekt ist nicht iterierbar

...da items immer definiert ist (als eingebaute Funktion).

Und das ist ein Bug, IMO. Die Daten['items'] sollten niemals mit den Builtins kollidieren. Mit anderen Worten, wenn Sie ein optionales "Items"-Attribut sauber durchlaufen möchten, müssen Sie es umbenennen. Im Idealfall sollte das nicht der Fall sein.

Warum sollte die JSON-Antwort keine Elemente enthalten? Ich bin mir nicht ganz sicher, ob ich das Problem hier sehe. Sicherlich wissen Sie im Voraus, was Ihre Daten sind. Sie können in Vorlagen auch bereits mit dem Operator in nach Schlüsseln in Wörterbüchern suchen.

Dieses Verhalten zu ändern ist völlig ausgeschlossen, da Tausende von Vorlagen kaputt gehen würden, wenn wir diese Attribut-/Elementunterstützung entfernen.

Ich schlage nicht vor, etwas fallen zu lassen, nur um es strenger zu machen.

Das Problem besteht heute darin, dass bei Verwendung der expliziten Nachschlagenotation: data['items'] eine eingebaute Wörterbuchfunktion gefunden wird und sie nicht . Python tut es nicht, und jinja sollte es auch nicht. Wenn man das eingebaute (oder irgendeine andere Funktion) aufrufen muss, haben Sie die aufrufende Notation dafür: data.items()

Ich bin mir nicht sicher, ob es sehr klar wäre, dort dict.items in den Sonderfall zu OrderedDict immer noch fehlschlagen würde. Ich denke, in diesen Fällen ist es besser, foo['items'] zu verwenden. Es ist nicht super hübsch, aber konsistentes Verhalten.

Der einzige mögliche "Fix/Hack", den ich mir vorstellen könnte, um dies auf eine schönere Weise zu beheben, besteht darin, zu überprüfen, ob ein einfaches Attribut (dh kein Funktionsaufruf) in einer Iteration verwendet wird und Sie versuchen, über etwas zu iterieren, das nicht iterierbar, aber aufrufbar ist . In diesem Fall ist es sehr unwahrscheinlich, dass das Ausprobieren des Elements einen vorhandenen Code knackt. Allerdings wäre die Implementierung für ein solches Verhalten in Jinja wahrscheinlich extrem schrecklich ;)

Warum ist das ein Sonderfall? Ich bitte Jinja, sich genau wie Python zu verhalten. Insbesondere sollte foo['items'] None ergeben, wenn es keinen vom Benutzer eingefügten Schlüssel "items" enthält. Heute greift Jinja darauf zurück, die gleichnamige eingebaute Funktion zu finden. Also einfach tun, was Python tut:

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

Übrigens, ich glaube auch, dass die alternative Notation "foo.items" auch gut gehandhabt werden sollte, da Sie "()" verwenden müssen, um einen Callable aufzurufen, aber wenn Sie eine gewisse Abwärtskompatibilität für foo.items benötigen, um entweder zu finden eine Taste drücken oder eine Funktion automatisch aufrufen, dann finde ich das schade.

Die Jinja-Logik für ['x'] und .x lautet wie folgt:

  • Überprüfen Sie, ob das item/attr (was auch immer Ihre Syntax in Python erhält) existiert, wenn ja, verwenden Sie das
  • Überprüfen Sie, ob das attr/item (das "Gegenteil" von dem, was Ihre Syntax in Python tun würde) existiert, wenn ja, verwenden Sie das
  • Fehlschlagen, wenn keines vorhanden ist

Also sucht x.items nach dem Attribut und da eines existiert, verwendet es dieses. Diese Logik funktioniert unabhängig vom Wettbewerb so. Um dies zu verbessern, müsste sich die Attributsuchlogik je nach Kontext ändern. Und selbst dann würde es zu unangenehmen Situationen wie diesem Code führen, der funktionieren würde:

{% for x in mydict.items %}

gegen diesen Code, der nicht funktionieren würde

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

Und die spezielle Behandlung in diesem Fall wäre einfach unmöglich, ohne zu verfolgen, woher eine Variable stammt.

@slisznia Sie bitten

Ah, ich habe nicht einmal den Vorschlag gesehen, dass foo['bar'] keine Attributsuche durchführt. Das ist sicher eine schlechte Idee! Was ist falsch daran, einfach for item in data.get('items', []) ?

@ThiefMaster das mydict.get('items', []) funktioniert natürlich und danke für den Hinweis auf eine andere Methode als das Umbenennen von Schlüsseln.

War diese Seite hilfreich?
0 / 5 - 0 Bewertungen