Перемещение нескольких списков в шаблоне django в одном и том же цикле

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

какое-то мышление ссылается на это:

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

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

{% endfor %}

Возможно ли подобное?

Ответ 1

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

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

Смотрите документацию

Ответ 2

Нарушение шаблонов 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 в своих представлениях.

Ответ 3

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

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

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

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

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