Traversing multiple lists in django template in same for loop

为君一笑 提交于 2021-02-06 10:14:07

问题


I want to traverse multiple lists within a django template in the same for loop. How do i do it?

some thinking link this:

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

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

{% endfor %}

Is something like this possible?


回答1:


You have two options:

1. You define your objects so that you can access the items like parameters

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

Note that you have to make up the list by combining the three lists:

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

2. You define your own filter

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

See the filter documentation




回答2:


Abusing django templates:

{% 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 %}

But NEVER do that!!! just use zip in Your views.




回答3:


Custom Template Tag

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)

Usage:

{% zip_longest list1 list2 as items %}

This lets you pass 2 or more lists to a tag then iterate over the items variable. If you use more than two lists then you'll need loop again unfortunately. However with two lists I've used the first and last filters inside the loop like this:

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

However, having built all of this, it might be better to do this in a view!

Python's Itertools

You should also consider Python's itertools, which has the izip_longest method that takes two or more lists. It returns the lists as one using the longest list to determine the size (if you want it to concatenate to the shortest list then look no further than izip). You can choose what to fill empty values with using the fillvalue keyword, but by default this is None.

Both izip_longest and izip return an iterator instead of a list, so you could see some performance gain on larger sites.

It's important to to note that izip_longest might hit the db slightly more than necessary depending on how it determines the length of each list (performing a count() would be an extra call to the db). However I haven't managed to reliably test this and it would only matter once you had to scale up.



来源:https://stackoverflow.com/questions/3132706/traversing-multiple-lists-in-django-template-in-same-for-loop

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!