web-dev-qa-db-fra.com

La portée d'une variable Jinja peut-elle s'étendre au-delà d'un bloc interne?

J'ai le modèle suivant Jinja: 

{% set mybool = False %}
{% for thing in things %}
    <div class='indent1'>
        <ul>
            {% if current_user %}
              {% if current_user.username == thing['created_by']['username'] %}
                {% set mybool = True %}
                <li>mybool: {{ mybool }}</li> <!-- prints True -->
                <li><a href='#'>Edit</a></li>
              {% endif %}
            {% endif %}
            <li>Flag</li>
        </ul>
    </div>
    <hr />
{% endfor %}

{% if not mybool %}
    <!-- always prints this -->
    <p>mybool is false!</p>
{% else %}
  <p>mybool is true!</p>
{% endif %}

Si la condition est remplie dans la boucle for, j'aimerais changer mybool en true pour pouvoir afficher mybool is true! ci-dessous. Cependant, il semble que la portée de la variable mybool intérieure soit limitée à l'instruction if, de sorte que la valeur souhaitéemybool n'est jamais définie. 

Comment définir la variable "globale" mybool afin de pouvoir l'utiliser dans la dernière instruction if

MODIFIER

J'ai trouvé quelques suggestions (seulement les pages vues en cache correctement), mais elles ne semblent pas fonctionner. Peut-être qu'ils sont obsolètes dans Jinja2 ...

MODIFIER

Solution fournie ci-dessous. Je suis toujours curieux de savoir pourquoi les suggestions ci-dessus ne fonctionnent pas. Est-ce que quelqu'un sait avec certitude qu'ils ont été déconseillés? 

51
Matt Norris

Une façon de contourner cette limitation est d'activer l'extension d'instruction d'expression "do" et d'utiliser un tableau au lieu d'un booléen

{% set exists = [] %}
{% for i in range(5) %}
      {% if True %}
          {% do exists.append(1) %}
      {% endif %}
{% endfor %}
{% if exists %}
    <!-- exists is true -->
{% endif %}

Pour activer l'extension de déclaration d'expression "do" de Jinja: e = jinja2.Environment(extensions=["jinja2.ext.do",])

46
Garrett

Réponse à une question connexe: je voulais avoir un compteur global du nombre de fois où j'ai entré un certain if-block dans le modèle et fini avec le ci-dessous.

En haut du modèle:

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

Dans le bloc if, je veux compter:

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

Lors de l'affichage du compte:

{{ counter|length }}

La chaîne '1' peut être remplacée par n'importe quelle chaîne ou chiffre, je crois. C'est toujours un hack, mais pas très grand.

14
Godsmith

Vous pouvez résoudre votre problème en utilisant ce hack (sans extensions):

import jinja2

env = jinja2.Environment()
print env.from_string("""
{% set mybool = [False] %}
{% for thing in things %}
    <div class='indent1'>
        <ul>
            {% if current_user %}
              {% if current_user.username == thing['created_by']['username'] %}
                {% set _ = mybool.append(not mybool.pop()) %}
                <li>mybool: {{ mybool[0] }}</li> <!-- prints True -->
                <li><a href='#'>Edit</a></li>
              {% endif %}
            {% endif %}
            <li>Flag</li>
        </ul>
    </div>
    <hr />
{% endfor %}

{% if not mybool[0] %}
    <!-- always prints this -->
    <p>mybool is false!</p>
{% else %}
  <p>mybool is true!</p>
{% endif %}
""").render(current_user={'username':'me'},things=[{'created_by':{'username':'me'}},{'created_by':{'username':'you'}}])
8
Alvaro Fuentes

Mise à jour 2018

À partir de Jinja 2.10 (8 nov. 2017), un objet namespace() permet de résoudre ce problème particulier. Voir le document officiel Assignments documentation pour plus de détails et un exemple; la documentation class montre ensuite comment affecter plusieurs valeurs à un espace de noms.

5
Jens

Lors de l'écriture de contextfunction() ou de quelque chose de similaire, vous avez peut-être remarqué que le contexte tente de vous empêcher de le modifier.

Si vous avez réussi à modifier le contexte à l’aide d’une API de contexte interne, vous avez peut-être remarqué que les modifications apportées au contexte ne semblaient pas visibles dans le modèle. La raison en est que Jinja utilise le contexte uniquement comme source de données principale pour les variables de modèle pour des raisons de performances.

Si vous souhaitez modifier le contexte, écrivez une fonction qui retourne une variable que vous pouvez affecter à une variable en utilisant set:

{% set comments = get_latest_comments() %}

Source

4
Shankar Cabus

Avait besoin de trouver le nombre maximum d'entrées dans un objet (objet) à partir d'une liste (objects_from_db), 

Cela n'a pas fonctionné pour des raisons connues dans jinja2 et à portée variable.

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

Voici ce qui fonctionne:

 {% 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] %}

J'espère que cela aidera quelqu'un d'autre à essayer de comprendre la même chose. 

3
Paddy V

Trouvé cet excellent article qui décrit un petit bidouillage. Il n'est pas possible de changer la valeur d'une variable jinja dans une autre portée, mais il est possible de modifier les valeurs d'un dictionnaire 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}}

J'ai également trouvé très utile cette syntaxe pour définir la valeur sans utiliser réellement set:

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

En fait, il vérifie le résultat d'une opération update sur un dictionnaire (note to self).

0
Maciejg