3

I'm building a django webpage but I seem to have hit a snag as I can't really figure out how to use the current iteration of a for loop (in the template) for multiple lists:

        {% for num in loopRange %}
    <tr>
        {% for num2 in subRange %}
        <td>{% cycle list1 list2 list3 list4 %}</td>
        {% endfor %}
    </tr>
    {% endfor %}

I found a couple questions here stackoverflow that were similar and I attempted to use cycle, but alas this just resulted in all members of the list being printed each time--not exactly unexpected but I can't figure out how for the life of me.

What I have is multiple lists that are all similar in content, each list is a column in a row. So, if it was python and I was concatenating strings it would be like this:

for i in xrange(5):
    string = list1[i] + list2[i] + list3[i] + list 4[i]

So basically that. I'm passing each list in as context in addition to two xranges (loopRange and Subrange in the first example), I need five rows (each list has five members) and four columns (four lists).

EDIT: I suppose in a nuthshell I want to refer to list indeces as foo[bar], done in django as foo.bar, however bar apparently can't be an integer an iterable range passed in as content

Thanks!

3 Answers 3

3

I'd say that the best approach would be to work on the data in the view before passing it to the template.

For example, the zip builtin can be used to create the rows list based on the lists that contain the columns:

rows = zip(list1, list2, list3, list4)

After that, the template can iterate over the rows one by one and access the columns using index access if needed:

{% for row in rows %}
  {{row.0}} {{row.1}} {{row.2}} {{row.3}}
{% endfor %}
Sign up to request clarification or add additional context in comments.

1 Comment

That solves it! Fantastic. zip() is a very handy function for something like this.
1

You can just create a simple filter to return an item from the list:

@register.filter
def get_item(row, i):
    try:
        return row[i]
    except IndexError:
        return ''

Then:

{{ list1|get_item:num2 }}

Comments

0

If you would really like to do this in your templates themselves for some reason, you can also write a custom template tag that does it for you:

#app/templatetags/zip.py
from django import template

register = template.Library()

class Zipper(template.Node):
    def __init__(self, format_string, var_name):
        self.format_string = format_string
        self.var_name = var_name
    def render(self, context):
        zippables = [context[tok] for tok in self.format_string.split()]
        import pdb; pdb.set_trace()
        context[self.var_name] = zip(*zippables)
        return ''

import re

@register.tag(name="zip")
def do_zip(parser, token):
    try:
        # Splitting by None == splitting by spaces.
        tag_name, arg = token.contents.split(None, 1)
    except ValueError:
        raise template.TemplateSyntaxError("%r tag requires arguments" % token.contents.split()[0])
    m = re.search(r'(.*?) as (\w+)', arg)
    if not m:
        raise template.TemplateSyntaxError("%r tag had invalid arguments" % tag_name)
    format_string, var_name = m.groups()
    return Zipper(format_string, var_name)

Note: this syntax is about to get a lot simpler in Django 1.4, due to the addition of assignment_tag.

Then, just use it like this:

{%load zip%}

{% zip list1 list2 list3 list4 as list_of_rows %}

You can then use list_of_rows wherever you want in that block, including inside two nested for loops.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.