La variable de configuración en Jinja para bucle no persiste entre iteraciones

Quiero recorrer una lista de objetos y contar cuántos objetos cumplen un requisito. Basé mi código en otros ejemplos que encontré, pero no funciona, el recuento siempre es 0 después del bucle.

Para cada casa, quiero recorrer cada habitación y contar cuántas habitaciones tienen una cama. Quiero dar salida a eso y luego reiniciar la cuenta para la siguiente casa.

{% for house in city %} {% set count = 0 %} 
{{ house.address }} has {{ count }} beds in it rooms.
{% for room in house %} {% if room.has_bed == True %}{% set count = count + 1 %}{% endif %} {% endfor %} {% endfor %}

Para Jinja 2.9, el comportamiento del scope fue corregido, invalidando el código que funcionaba en versiones anteriores. El valor incrementado del count solo vive dentro del scope del bucle. Su ejemplo involucra establecer variables, pero el concepto es el mismo:

Tenga en cuenta que no es posible establecer variables dentro de un bloque y hacer que aparezcan fuera de él. Esto también se aplica a los bucles. La única excepción a esa regla es si las declaraciones que no introducen un scope. Como resultado, la siguiente plantilla no va a hacer lo que podría esperar:

 {% set iterated = false %} {% for item in seq %} {{ item }} {% set iterated = true %} {% endfor %} {% if not iterated %} did not iterate {% endif %} 

No es posible con la syntax de Jinja hacer esto.

Tendrá que hacer una solución alternativa para realizar un seguimiento del count través de iteraciones. Establece una lista, adjúntala y luego cuenta su longitud.

 {% for house in city %} {% set room_count = [] %} {% for room in house %} {% if room.has_bed %} {% if room_count.append(1) %}{% endif %} {% endif %} {% endfor %} 
{{ house.address }} has {{ room_count|length }} beds.
{% endfor %}

Jinja 2.10 introduce el objeto de namespace para manejar la asignación y la comparación en bucles.

 {% set ns = namespace(beds=0) %} {% for room in house %} {% if room.has_bed %} {% set ns.beds = ns.beds + 1 %} {% endif %} {% endfor %} {{ house.address }} has {{ ns.beds }} beds. 

Normalmente, el set no maneja los atributos, por lo que las respuestas anteriores mutan los objetos con métodos. namespace ha sido encuadrado de manera especial para que los atributos de configuración funcionen

La razón por la que el contador original no funcionó es debido a las reglas de scope de Jinja. A diferencia de Python, la mayoría de los bloques son nuevos ámbitos. set siempre define una variable local, excepto en este nuevo caso especial de namespace.attribute .


En este caso específico, puede lograr lo que quiera con los filtros.

 {% set beds = house.rooms|selectattr('has_bed')|length %} {{ house.address }} has {{ beds }} beds. 

Sin embargo, hay casos en los que se necesita almacenar información en todos los ámbitos. Por ejemplo, si desea generar cierta información además de incrementar el contador, tendría sentido utilizar un namespace .

Para Jinja <= 2.8, el código que has mostrado funciona. Sin embargo, esto se debió a un comportamiento incorrecto en las reglas de alcance de Jinja, que se corrigió en 2.9.

 {% for house in city %} {% set count = 0 %} {% for room in house %} {% if room.has_bed %} {% set count = count + 1 %} {% endif %} {% endfor %} 
{{ house.address }} has {{ count }} beds.
{% endfor %}