在django模板中遍历同一for循环中的多个列表

时间:2010-06-28 13:24:00

标签: django list django-templates

我想在同一个for循环中遍历django模板中的多个列表。我该怎么做?

一些思考链接:

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

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

{% endfor %}

这样的事情可能吗?

3 个答案:

答案 0 :(得分:22)

您有两种选择:

<强> 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

请参阅filter documentation

答案 1 :(得分:6)

滥用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。

答案 2 :(得分:1)

自定义模板标记

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关键字选择填充空值的内容,但默认情况下为无。

izip_longest和izip都返回迭代器而不是列表,因此您可以在较大的站点上看到一些性能提升。

重要的是要注意izip_longest可能会略微超过必要的数量,具体取决于它如何确定每个列表的长度(执行count()将是对数据库的额外调用)。但是我还没有成功地对此进行测试,只有在你不得不扩大规模时才会这样做。