¿Puede el scope de una variable Jinja extenderse más allá en un bloque interno?

Tengo la siguiente plantilla de Jinja:

{% set mybool = False %} {% for thing in things %} 
    {% if current_user %} {% if current_user.username == thing['created_by']['username'] %} {% set mybool = True %}
  • mybool: {{ mybool }}
  • Edit
  • {% endif %} {% endif %}
  • Flag

{% endfor %} {% if not mybool %}

mybool is false!

{% else %}

mybool is true!

{% endif %}

Si la condición se cumple en el ciclo for , me gustaría cambiar mybool a true para que pueda mostrar mybool is true! abajo. Sin embargo, parece que el scope del mybool interno está limitado a la instrucción if , por lo que nunca se establece el mybool deseado .

¿Cómo puedo configurar el mybool “global” para que pueda usarlo en la última instrucción if ?

EDITAR

He encontrado algunas sugerencias (solo las visitas a la página en caché correctamente), pero parece que no funcionan. Tal vez están en desuso en Jinja2 …

EDITAR

Solución proporcionada a continuación. Todavía tengo curiosidad de por qué las sugerencias anteriores no funcionan. ¿Alguien sabe con certeza que estaban en desuso?

Una forma de evitar esta limitación es habilitar la extensión de statement de expresión “do” y usar una matriz en lugar de booleana:

 {% set exists = [] %} {% for i in range(5) %} {% if True %} {% do exists.append(1) %} {% endif %} {% endfor %} {% if exists %}  {% endif %} 

Para habilitar la extensión de statement de expresión “do” de Jinja: e = jinja2.Environment(extensions=["jinja2.ext.do",])

Respuesta a una pregunta relacionada: quería tener un contador global del número de veces que ingresé a un cierto bloque de if en la plantilla, y terminé con el siguiente.

En la parte superior de la plantilla:

 {% set counter = ['1'] %} 

En el bloque if quiero contar:

 {% if counter.append('1') %}{% endif %} 

Al mostrar el conteo:

 {{ counter|length }} 

La cadena '1' puede reemplazarse con cualquier cadena o dígito, creo. Todavía es un truco, pero no muy grande.

Puedes resolver tu problema usando este truco (sin extensiones):

 import jinja2 env = jinja2.Environment() print env.from_string(""" {% set mybool = [False] %} {% for thing in things %} 
    {% if current_user %} {% if current_user.username == thing['created_by']['username'] %} {% set _ = mybool.append(not mybool.pop()) %}
  • mybool: {{ mybool[0] }}
  • Edit
  • {% endif %} {% endif %}
  • Flag

{% endfor %} {% if not mybool[0] %}

mybool is false!

{% else %}

mybool is true!

{% endif %} """).render(current_user={'username':'me'},things=[{'created_by':{'username':'me'}},{'created_by':{'username':'you'}}])

Al escribir una función de contextfunction() o algo similar, habrá notado que el contexto intenta evitar que lo modifique.

Si ha logrado modificar el contexto utilizando una API de contexto interno, puede haber notado que los cambios en el contexto no parecen estar visibles en la plantilla. La razón de esto es que Jinja usa el contexto solo como fuente de datos primaria para variables de plantilla por razones de rendimiento.

Si desea modificar el contexto, escriba una función que devuelva una variable que pueda asignarse a una variable utilizando set:

{% set comments = get_latest_comments() %}

Fuente

Tuve la necesidad de encontrar el número máximo de entradas en un objeto (objeto) de una lista (objects_from_db),

Esto no funcionó por razones conocidas en jinja2 y scope variable.

  {% set maxlength = 0 %} {% for object in objects_from_db %} {% set ilen = object.entries | length %} {% if maxlength < ilen %} {% set maxlength = ilen %} {% endif %} {% endfor %} 

Esto es lo que funciona:

  {% set mlength = [0]%} {% for object in objects_from_db %} {% set ilen = object.entries | length %} {% if mlength[0] < ilen %} {% set _ = mlength.pop() %} {% set _ = mlength.append(ilen)%} {% endif %} {% endfor %} {% set maxlength = mlength[0] %} 

Espero que esto ayude a alguien más a tratar de descubrir lo mismo.

Actualización 2018

A partir de Jinja 2.10 (8 de noviembre de 2017), hay un objeto namespace() para resolver este problema en particular. Consulte la documentación oficial de Asignaciones para obtener más detalles y un ejemplo; la documentación de la class ilustra cómo asignar varios valores a un espacio de nombres.

Encontré este gran artículo que describe un pequeño truco. No es posible cambiar el valor de una variable jinja en un ámbito diferente, pero es posible modificar los valores de un diccionario global:

 # works because dictionary pointer cannot change, but entries can {% set users = ['alice','bob','eve'] %} {% set foundUser = { 'flag': False } %} initial-check-on-global-foundUser: cmd.run: name: echo initial foundUser = {{foundUser.flag}} {% for user in users %} {%- if user == "bob" %} {%- if foundUser.update({'flag':True}) %}{%- endif %} {%- endif %} echo-for-{{user}}: cmd.run: name: echo my name is {{user}}, has bob been found? {{foundUser.flag}} {% endfor %} final-check-on-global-foundUser: cmd.run: name: echo final foundUser = {{foundUser.flag}} 

También encontré muy útil esta syntax para establecer el valor sin usar el set :

 {%- if foundUser.update({'flag':True}) %}{%- endif %} 

En realidad, comprueba el resultado de una operación de update en un diccionario (nota a sí mismo).