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