Пользовательский тег шаблона
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 %}
Однако, построив все это, может быть, лучше сделать это в виде!
Itertools Python
Вам также следует рассмотреть itertools в Python, который имеет метод izip_longest , который принимает два или более списков. Он возвращает списки как один, используя самый длинный список для определения размера (если вы хотите, чтобы он соединялся с самым коротким списком, тогда смотрите не дальше, чем izip ). Вы можете выбрать, что заполнять пустые значения, используя ключевое слово fillvalue
, но по умолчанию это None.
И izip_longest, и izip возвращают итератор вместо списка, так что вы можете увидеть некоторое увеличение производительности на крупных сайтах.
Важно отметить, что izip_longest может ударить по БД немного больше, чем необходимо, в зависимости от того, как он определяет длину каждого списка (выполнение count () будет дополнительным вызовом для БД). Однако мне не удалось надежно протестировать это, и это будет иметь значение только после того, как вы увеличите масштаб.