Wie kann ich einen einfachen Zähler mit Jinja2 Vorlagen machen?
Frage
Ich habe zwei for-Schleifen, die beide gleichermaßen in Würde. Ich möchte einen Zähler während jeder inneren Iteration erhöht haben.
Zum Beispiel, betrachten Sie diese Vorlage:
from jinja2 import Template
print Template("""
{% set count = 0 -%}
{% for i in 'a', 'b', 'c' -%}
{% for j in 'x', 'y', 'z' -%}
i={{i}}, j={{j}}, count={{count}}
{% set count = count + 1 -%}
{% endfor -%}
{% endfor -%}
""").render()
sollte nicht dieser Druck count=0
durch count=8
? Nein, es funktioniert nicht.
i=a, j=x, count=0
i=a, j=y, count=1
i=a, j=z, count=2
i=b, j=x, count=0
i=b, j=y, count=1
i=b, j=z, count=2
i=c, j=x, count=0
i=c, j=y, count=1
i=c, j=z, count=2
Was soll das?
. Hinweis: Ich kann nicht einfach speichern die äußere loop
Variable, um den Zähler zu berechnen, weil in meiner Software, die Anzahl der inneren Iterationen Variable
Lösung
Mit variabler Innengruppengrößen, dies funktionieren wird:
from jinja2 import Template
items = [
['foo', 'bar'],
['bax', 'quux', 'ketchup', 'mustard'],
['bacon', 'eggs'],
]
print Template("""
{% set counter = 0 -%}
{% for group in items -%}
{% for item in group -%}
item={{ item }}, count={{ counter + loop.index0 }}
{% endfor -%}
{% set counter = counter + group|length %}
{% endfor -%}
""").render(items=items)
... welche druckt:
item=foo, count=0
item=bar, count=1
item=bax, count=2
item=quux, count=3
item=ketchup, count=4
item=mustard, count=5
item=bacon, count=6
item=eggs, count=7
Ich denke, Variablen außerhalb up mehr erklärt als eine Ebene der Anwendungsbereich nicht auf oder etwas zugeordnet werden können.
Andere Tipps
Es sieht wie ein Fehler, aber wie über einige dieser Berechnung außerhalb der Vorlage zu verschieben?
from jinja2 import Template
outer_items = list(enumerate("a b c".split()))
inner_items = list(enumerate("x y z".split()))
print Template("""
{% for outer, i in outer_items -%}
{% for inner, j in inner_items -%}
{% set count = outer * num_outer + inner -%}
i={{i}}, j={{j}}, count={{count}}
{% endfor -%}
{% endfor -%}
""").render(outer_items=outer_items,
inner_items=inner_items,
num_outer=len(outer_items))
Ausgabe:
i=a, j=x, count=0
i=a, j=y, count=1
i=a, j=z, count=2
i=b, j=x, count=3
i=b, j=y, count=4
i=b, j=z, count=5
i=c, j=x, count=6
i=c, j=y, count=7
i=c, j=z, count=8
Um Anwendungsfälle wie dieser, ich habe ein kleines Umgebung Filter, das zählt Vorkommen eines Schlüssels.
zu lösenHier ist de-Code (mit doc-Test) von myfilters.py:
#coding: utf-8
from collections import defaultdict
from jinja2 import environmentfilter
from jinja2.utils import soft_unicode
@environmentfilter
def inc_filter(env, key, value=1, result='value', reset=False):
"""
Count ocurrences of key.
Stores the counter on Jinja's environment.
>>> class Env: pass
>>> env = Env()
>>> inc_filter(env, 'x')
1
>>> inc_filter(env, 'x')
2
>>> inc_filter(env, 'y')
1
>>> inc_filter(env, 'x')
3
>>> inc_filter(env, 'x', reset=True)
1
>>> inc_filter(env, 'x')
2
>>> inc_filter(env, 'x', value=0, reset=True)
0
>>> inc_filter(env, 'x', result=None)
>>> inc_filter(env, 'x', result=False)
u''
>>> inc_filter(env, 'x', result='key')
'x'
>>> inc_filter(env, 'x')
4
"""
if not hasattr(env, 'counters'):
env.counters = defaultdict(int)
if reset:
env.counters[key] = 0
env.counters[key] += value
if result == 'key':
return key
elif result == 'value':
return env.counters[key]
elif result == None:
return None
else:
return soft_unicode('')
## Module doctest
if __name__ == '__main__':
import doctest
doctest.testmod()
Setup Ihre Umgebung unserer eigenen Filter Registrierung:
#coding: utf-8
from jinja2 import Environment, FileSystemLoader
from myfilters import inc_filter
env = Environment(loader=loader=FileSystemLoader('path'))
env.filters['inc'] = inc_filter
t = env.get_template('yourtemplate.txt')
items = [
['foo', 'bar'],
['bax', 'quux', 'ketchup', 'mustard'],
['bacon', 'eggs'],
]
res = t.render(items=items)
Und auf die Vorlage, verwenden Sie es wie folgt aus:
{% for group in items -%}
{% for item in group -%}
item={{ item }}, count={{ 'an_identifier'|inc }}
{% endfor -%}
{% endfor -%}
... welche druckt:
item=foo, count=0
item=bar, count=1
item=bax, count=2
item=quux, count=3
item=ketchup, count=4
item=mustard, count=5
item=bacon, count=6
item=eggs, count=7
Es gibt builtin globale Funktion Cycler () Bereitstellung Schleife unabhängigen Wert Radfahren. Mit der gleichen Idee können Sie Ihre eigene counter()
Funktion wie folgt definiert werden:
env=Environment(...) # create environment
env.globals['counter']=_Counter # define global function
env.get_template(...).render(...) # render template
Hier ist die Klasse, die Geräte die Funktion:
class _Counter(object):
def __init__(self, start_value=1):
self.value=start_value
def current(self):
return self.value
def next(self):
v=self.value
self.value+=1
return v
Und hier ist, wie es zu benutzen:
{% set cnt=counter(5) %}
item #{{ cnt.next() }}
item #{{ cnt.next() }}
item #{{ cnt.next() }}
item #{{ cnt.next() }}
Es ist Gonna zu machen:
item #5
item #6
item #7
item #8
Keine Notwendigkeit, einen Zähler hinzuzufügen. Sie können die äußere Schleife-Index wie folgt zugreifen:
{% for i in 'a', 'b', 'c' -%}
{% set outerloop = loop %}
{% for j in 'x', 'y', 'z' -%}
i={{i}}, j={{j}}, count={{outerloop.index0 * loop|length + loop.index0}}
{% endfor -%}
{% endfor -%}