Как я могу сделать простой счетчик с шаблонами Jinja2? - PullRequest
13 голосов
/ 14 августа 2010

У меня есть два цикла, оба одинаково достойны. Я хотел бы увеличить счетчик во время каждой внутренней итерации.

Например, рассмотрим этот шаблон:

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 ]

15 голосов
/ 15 августа 2010

С переменными размерами внутренней группы это будет работать:

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

Я полагаю, переменные, объявленные вне более чем одного уровня области видимостине может быть назначен или что-то.

3 голосов
/ 12 декабря 2011

Для решения таких случаев использования, как этот, я написал небольшой фильтр среды, который подсчитывает вхождения ключа.

Вот код 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
3 голосов
/ 14 августа 2010

Это похоже на ошибку, но как насчет перемещения некоторых из этих расчетов за пределы шаблона?

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
1 голос
/ 14 ноября 2015

Имеется встроенная глобальная функция cycler () , обеспечивающая циклически независимое значение цикла. Используя ту же идею, вы можете определить свою собственную функцию counter() следующим образом:

env=Environment(...) # create environment
env.globals['counter']=_Counter # define global function
env.get_template(...).render(...) # render template

Вот класс, который реализует функцию:

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

А вот как это использовать:

{% set cnt=counter(5) %}
item #{{ cnt.next() }}
item #{{ cnt.next() }}
item #{{ cnt.next() }}
item #{{ cnt.next() }}

Будет отображаться:

item #5
item #6
item #7
item #8
0 голосов
/ 02 марта 2016

Нет необходимости добавлять счетчик.Вы можете получить доступ к индексу внешнего цикла следующим образом:

{% 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 -%}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...