Jinja2: Ändern Sie den Wert einer Variablen innerhalb einer Schleife

74

Ich möchte den Wert der außerhalb der Schleife deklarierten Variablen innerhalb einer Schleife ändern. Wenn Sie sich jedoch ständig ändern, bleibt der Anfangswert außerhalb der Schleife.

{% set foo = False %}

{% for item in items %}
  {% set foo = True %}
  {% if foo %} Ok(1)! {% endif %}
{% endfor %}

{% if foo %} Ok(2)! {% endif %}

Dies macht:

Ok(1)!

Die einzige (schlechte) Lösung, die bisher gefunden wurde, war folgende:

{% set foo = [] %}

{% for item in items %}
  {% if foo.append(True) %} {% endif %}
  {% if foo %} Ok(1)! {% endif %}
{% endfor %}

{% if foo %} Ok(2)! {% endif %}

Dies macht:

Ok(1)!
Ok(2)!

Aber es ist sehr hässlich! Gibt es eine andere elegantere Lösung?

Shankar Cabus
quelle
3
Ich glaube nicht, dass es einen anderen Weg gibt. Vielleicht könnten Sie den Code so umstrukturieren, dass Sie die Variable nicht festlegen müssen.
Alex Morega
2
+1 für die Frage, als sie für mich beantwortet wurde :)
Glen Swift
1
@ Shankar Cabus: tolle Frage. Dies sollte wahrscheinlich unterJinja Annoyances
dreftymac
Ich denke, diese Frage ist dupliziert in: stackoverflow.com/questions/7537439/… und stackoverflow.com/questions/4870346/… (gerade erst gestartet , kann die Frage nicht kennzeichnen) Sie können Pashkas Ansatz verwenden und hinzufügen jinja2.ext.do, um sie zu bereinigen ein bisschen
Gerardo Roza
Ich fand, dass dieser Code die einzige Möglichkeit ist, ein Konstrukt zu umgehen, das ich in salt + jinja nicht verwenden konnte: somelist | map (format) | join
Martin

Antworten:

75

Versuchen Sie auch einen wörterbuchbasierten Ansatz. Es scheint weniger hässlich zu sein.

{% set vars = {'foo': False} %}

{% for item in items %}
  {% if vars.update({'foo': True}) %} {% endif %}
  {% if vars.foo %} Ok(1)! {% endif %}
{% endfor %}

{% if vars.foo %} Ok(2)! {% endif %}

Dies macht auch:

Ok(1)!
Ok(2)!
Pashka
quelle
8
Immer noch hässlich, aber es funktioniert. Ich bin ziemlich überrascht, dass es mit jinja2 keinen pythonischen Weg gibt, dies zu tun.
kramer65
1
Auf jeden Fall ein bisschen sauberer, besonders wenn Sie mehr als eine Variable benötigen
Ade Miller
1
TLDR, funktioniert set varseinfach nicht in einer for-Schleife?
ThorSummoner
3
@ kramer65: Lösungen scheinen in Arbeit zu sein: github.com/pallets/jinja/pull/684 ; github.com/pallets/jinja/pull/676
Michael Scheper
@ThorSummoner scheint es. Ich habe viele Leute gehört, die Python lobten. Sie kochen auch nur mit Wasser.
Toskan
42

wie in der Dokumentation erwähnt:

Bitte beachten Sie, dass Zuweisungen in Schleifen am Ende der Iteration gelöscht werden und den Schleifenbereich nicht überleben können.

Ab Version 2.10 können Sie jedoch Namespaces verwenden:

    {% set ns = namespace(foo=false) %}      
    {% for item in items %}
      {% set ns.foo = True %}
      {% if ns.foo %} Ok(1)! {% endif %}
    {% endfor %}

    {% if ns.foo %} Ok(2)! {% endif %}
Omer
quelle
2
Wo Sie sagen, namespace(foo=false)ist der Kleinbuchstabe f in False eine jinja2-Umgangssprache oder meinten Sie False, wie Python es für boolesche Werte erfordert?
Malan88
4
Der Kleinbuchstabe false ist Teil von Jinjas Konvention: " The special constants true, false, and none are indeed lowercase. Because that caused confusion in the past, (True used to expand to an undefined variable that was considered false), all three can now also be written in title case (True, False, and None). However, for consistency, (all Jinja identifiers are lowercase) you should use the lowercase versions."
Omer
0

Sie können dies tun, um den Vorlagencode zu bereinigen

{% for item in items %}
  {{ set_foo_is_true(local_vars) }}
  {% if local_vars.foo %} Ok(1)! {% endif %}
{% endfor %}
{% if local_vars.foo %} Ok(2)! {% endif %}

Und im Servercode verwenden

items = ['item1', 'item2', 'item3']
#---------------------------------------------
local_vars = { 'foo': False }
def set_foo_is_true(local_vars):
  local_vars['foo'] = True
  return ''
env.globals['set_foo_is_true'] = set_foo_is_true    
#---------------------------------------------
return env.get_template('template.html').render(items=items, local_vars=local_vars)

Dies könnte auf Folgendes verallgemeinert werden

{{ set_local_var(local_vars, "foo", False) }}
{% for item in items %}
  {{ set_local_var(local_vars, "foo", True) }}
  {% if local_vars.foo %} Ok(1)! {% endif %}
{% endfor %}
{% if local_vars.foo %} Ok(2)! {% endif %}

Und im Servercode verwenden

items = ['item1', 'item2', 'item3']
#---------------------------------------------
local_vars = { 'foo': False }
def set_local_var(local_vars, name, value):
  local_vars[name] = value
  return ''
env.globals['set_local_var'] = set_local_var
#---------------------------------------------
return env.get_template('template.html').render(items=items, local_vars=local_vars)
Daniel F.
quelle