Как я могу сделать простой счетчик с шаблонами Jinja2?
у меня есть две петли, обе одинаковые по достоинству. Я бы хотел, чтобы счетчик увеличивался во время каждой внутренней итерации.
например, рассмотрим следующий шаблон:
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()
не должна ли эта печать count=0
через count=8
? нет, это не так.
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
что это дает?
Примечание: я не могу просто сохранить внешний loop
переменная для вычисления счетчика, потому что в моем программном обеспечении количество внутренних итераций является переменной.
5 ответов
с переменными размерами внутренней группы это будет работать:
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)
...который печатает:
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
Я думаю, что переменные, объявленные вне более чем одного уровня области, не могут быть назначены или что-то еще.
это похоже на ошибку, но как насчет перемещения некоторых из этих вычислений за пределы шаблона?
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))
выход:
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
чтобы решить такие случаи использования, как этот, я написал небольшой фильтр среды, который подсчитывает появления ключа.
вот код de (с тестом doc) 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()
настройте свою среду, зарегистрировав наш пользовательский фильтр:
#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)
и на вашем шаблоне используйте его следующим образом:
{% for group in items -%}
{% for item in group -%}
item={{ item }}, count={{ 'an_identifier'|inc }}
{% endfor -%}
{% endfor -%}
...который печатает:
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
существует встроенная глобальная функция велосипедист() обеспечивать петл-независимый задействовать значения. Используя ту же идею, вы можете определить свой собственный
нет необходимости добавлять счетчик. Вы можете получить доступ к индексу внешнего цикла следующим образом:
{% 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 -%}