Jinja: Imposible iterar sobre el objeto con el elemento "elementos"

Creado en 9 ene. 2017  ·  10Comentarios  ·  Fuente: pallets/jinja

Un servidor está devolviendo este JSON:
{
"artículos": [1,2,3]
...
}

Un intento de iterar sobre 'elementos' produce:
{% para el en data.items%}
....
TypeError: el objeto 'builtin_function_or_method' no es iterable

Lo anterior sucede porque Python dict tiene una función incorporada "items ()".

Como temp. Como solución temporal, instalé un gancho y cambié el nombre de las claves en la respuesta JSON de "elementos" a "__items" y luego se lo pasé a Jinja2. En ese punto, estoy iterando sobre el atributo renombrado bien:
{% para el en datos .__ elementos%}

Idealmente, Jinja2 debería ignorar las funciones integradas en for-loop o if-statement a menos que se invoque específicamente con una notación de función ().

Comentario más útil

Puede usar {% for el in data['items'] %} para priorizar el artículo. Esto también se cubre en la documentación. http://jinja.pocoo.org/docs/2.9/templates/#variables

Todos 10 comentarios

Puede usar {% for el in data['items'] %} para priorizar el artículo. Esto también se cubre en la documentación. http://jinja.pocoo.org/docs/2.9/templates/#variables

Fallará si la respuesta JSON ocasionalmente no incluye los 'elementos' (digamos que era un atributo opcional de la respuesta). En ese caso, {% for el in data ['items']%} también producirá:
TypeError: el objeto 'builtin_function_or_method' no es iterable

... ya que los elementos siempre se definen (como una función incorporada).

Y esto es un error, en mi opinión. Los datos ['elementos'] nunca deben colisionar con las incorporaciones. En otras palabras, si desea iterar limpiamente un atributo de "elementos" opcional, se ve obligado a cambiarle el nombre. Idealmente, ese no debería ser el caso.

¿Por qué la respuesta JSON no incluiría elementos? No estoy del todo seguro de ver el problema aquí. Seguro que sabes de antemano cuáles son tus datos. También puede verificar en las plantillas las claves con el operador in en los diccionarios.

Cambiar este comportamiento está completamente fuera de discusión porque miles de plantillas se romperían si elimináramos este atributo / soporte de elementos.

No estoy sugiriendo dejar nada, solo para hacerlo más estricto.

El problema actual es que cuando usa la notación de búsqueda explícita: data['items'] encuentra una función de diccionario incorporada y no debería . Python no lo hace, ni debería hacerlo jinja. Si uno necesita llamar a la función incorporada (o cualquier función para el caso), tiene la notación de llamada para ello: data.items()

No estoy seguro de si quedaría muy claro el caso especial dict.items allí. Especialmente porque todavía fallaría para tipos personalizados tipo dict o tal vez incluso OrderedDict . Creo que es mejor foo['items'] en esos casos. No es un comportamiento super bonito pero consistente.

El único "arreglo / pirateo" posible que podría imaginar para manejar esto de una manera más agradable es verificar si un atributo simple (es decir, no una llamada de función) se usa en una iteración y está tratando de iterar sobre algo que no es iterable sino que se puede llamar . En este caso, es muy poco probable que probar el elemento rompa el código existente. Sin embargo, la implementación de tal comportamiento en Jinja probablemente sería extremadamente terrible;)

¿Por qué es este un caso especial? Estoy pidiendo que Jinja se comporte exactamente como lo hace Python. Específicamente, foo ['elementos'] debería producir Ninguno si no hay "elementos" clave insertados por el usuario en él. Hoy, Jinja vuelve a buscar la función incorporada del mismo nombre. Así que simplemente haz lo que hace Python:

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

Por cierto, también creo que la notación alternativa "foo.items" debería manejarse bien también, ya que necesita usar "()" para llamar a un invocable, pero si necesita alguna compatibilidad con versiones anteriores para foo.items para encontrar una tecla o llamar a una función automáticamente, entonces supongo que es una lástima.

La lógica de Jinja para ['x'] y .x es la siguiente:

  • Verifique si el elemento / atributo (cualquiera que sea su sintaxis en Python) existe, si es así, use eso
  • Verifique si el atributo / elemento (el "opuesto" de lo que haría su sintaxis en Python) existe, si es así, use eso
  • Fallar si ninguno existe

Entonces x.items busca el atributo y, dado que existe, lo usa. Esta lógica funciona así sin importar el concurso. Mejorar esto requeriría que la lógica de búsqueda de atributos cambie según su contexto. E incluso entonces daría lugar a situaciones incómodas como este código que funcionaría:

{% for x in mydict.items %}

vs este código que no funcionaría

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

Y mantener un manejo especial en este caso sería simplemente imposible sin hacer un seguimiento de la procedencia de una variable.

@slisznia , esencialmente está pidiendo que Jinja2 cambie por completo el sistema de búsqueda de atributos que ha estado en funcionamiento durante aproximadamente 7 años en este momento.

Ah, ni siquiera vi la sugerencia de que foo['bar'] no realizara una búsqueda de atributos. ¡Seguro que es una mala idea! ¿Qué pasa con que simplemente uses for item in data.get('items', []) ?

@ThiefMaster, el mydict.get('items', []) funciona, por supuesto, y gracias por señalar otro método que no sea cambiar el nombre de las claves.

¿Fue útil esta página
0 / 5 - 0 calificaciones