Обход нескольких списков в шаблоне django в одном цикле for

Я хочу пройти несколько списков в шаблоне django в том же цикле for. Как мне это сделать?

некоторые мысли связывают это:

{% for item1, item2, item3 in list1, list2 list3 %}

{{ item1 }}, {{ item2 }}, {{ item3 }}

{% endfor %}

возможно ли что-то подобное?

3 ответов


у вас есть два варианта:

1. Вы определяете свои объекты, чтобы получить доступ к таким элементам, как параметры

for x in list:
    {{x.item1}}, {{x.item2}}, {{x.item3}}

обратите внимание, что вы должны составить список, объединив три списка:

lst = [{'item1': t[0], 'item2': t[1], 'item3':t[2]} for t in zip(list_a, list_b, list_c)]

2. Вы определяете свой собственный фильтр

from django import template

register = template.Library()

@register.filter(name='list_iter')
def list_iter(lists):
    list_a, list_b, list_c = lists

    for x, y, z in zip(list_a, list_b, list_c):
        yield (x, y, z)

# test the filter
for x in list_iter((list_a, list_b, list_c)):
    print x

посмотреть документация фильтр


злоупотребление шаблонами django:

{% for x in list_a %}
{% with forloop.counter|cut:" " as index %}
  {{ x }},
  {{ list_b|slice:index|last }},
  {{ list_c|slice:index|last }} <br/>
{% endwith %}
{% endfor %}

но никогда не делайте этого!!! просто используйте zip в своих представлениях.


Пользовательский Шаблон Тег

from django import template

register = template.Library()

def parse_tokens(parser, bits):
    """
    Parse a tag bits (split tokens) and return a list on kwargs (from bits of the  fu=bar) and a list of arguments.
    """

    kwargs = {}
    args = []
    for bit in bits[1:]:
        try:
            try:
                pair = bit.split('=')
                kwargs[str(pair[0])] = parser.compile_filter(pair[1])
            except IndexError:
                args.append(parser.compile_filter(bit))
        except TypeError:
            raise template.TemplateSyntaxError('Bad argument "%s" for tag "%s"' % (bit, bits[0]))

    return args, kwargs

class ZipLongestNode(template.Node):
    """
    Zip multiple lists into one using the longest to determine the size

    Usage: {% zip_longest list1 list2 <list3...> as items %}
    """
    def __init__(self, *args, **kwargs):
        self.lists = args
        self.varname = kwargs['varname']

    def render(self, context):
        lists = [e.resolve(context) for e in self.lists]

        if self.varname is not None:
            context[self.varname] = [i for i in map(lambda *a: a, *lists)]
        return ''

@register.tag
def zip_longest(parser, token):
    bits = token.contents.split()
    varname = None
    if bits[-2] == 'as':
        varname = bits[-1]
        del bits[-2:]
    else:
        # raise exception
        pass
    args, kwargs = parse_tokens(parser, bits)

    if varname:
        kwargs['varname'] = varname

    return ZipLongestNode(*args, **kwargs)

использование:

{% zip_longest list1 list2 as items %}

Это позволяет передавать 2 или более списков в тег, а затем перебирать переменную items. Если вы используете более двух списков, вам снова понадобится цикл, к сожалению. Однако с двумя списками я использовал первый и последний фильтры внутри цикла следующим образом:

{% for item in items %}
    {% with item|first as one %}
    {% with item|last as two %}
    <p>{{ one }}</p>
    <p>{{ two }}</p>
    {% endwith %}
    {% endwith %}
{% endfor %}

однако, построив все это, может быть, лучше сделать это в виде!

в Python Модуле itertools

вы также должны рассмотреть itertools Python, который имеет izip_longest метод, который принимает два или более списков. Он возвращает списки как один, используя самый длинный список для определения размера (если вы хотите, чтобы он объединялся с самым коротким списком, то смотрите не дальше izip функций). Вы можете выбрать, что заполнить пустые значения с помощью fillvalue ключевое слово, но по умолчанию это None.

оба izip_longest и izip возвращают итератор вместо списка, чтобы вы могли видеть некоторый прирост производительности на больших сайтах.

важно отметить, что izip_longest может ударить по БД немного больше, чем необходимо, в зависимости от того, как он определяет длину каждого списка (выполнение count() будет дополнительным вызовом БД). Однако мне не удалось надежно проверить это, и это будет иметь значение только после того, как вам придется масштабироваться.