Обход нескольких списков в шаблоне 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() будет дополнительным вызовом БД). Однако мне не удалось надежно проверить это, и это будет иметь значение только после того, как вам придется масштабироваться.