Can you just pass in QuerySet[::2] and QuerySet[1::2]?
Cheers,
Cliff
<table>
{% for d in data %}
{% if forloop.counter0|divisibleby:"2" %}<tr>{% endif %}
<td>{{d}}</td>
{% if forloop.counter|divisibleby:"2" %}</tr>{% endif %}
{% endfor %}
{% if data|length|divisibleby:"2" %}{% else
%}<td> </td></tr>{% endif %}
</table>
It's a little awkward, and doesn't generalize to more than two columns,
but it does produce properly structured HTML.
If you're willing to add some code to your view, you can get more columns:
In the view:
context['data'] = data
ncols = 3
for i, d in enumerate(data):
d.firstcol = (i % ncols == 0)
d.lastcol = (i % ncols == ncols-1)
context['data_blanks'] = [None]*((ncols-len(data)%ncols)%ncols)
In the template:
<table>
{% for d in data %}
{% if d.firstcol %}<tr>{% endif %}
<td>{{d}}</td>
{% if d.lastcol %}</tr>{% endif %}
{% endfor %}
{% for blank in data_blanks %}<td> </td>{% endfor %}
{% if data_blanks %}</tr>{% endif %}
</table>
--Ned.
http://nedbatchelder.com
--
Ned Batchelder, http://nedbatchelder.com
It can be generalized, as I've done it in a pinch, and it looks
something like this (untested, as I don't have my actual code
right in front of me):
<table>
<tr>
{% for d in data %}
<td>{{d}}</td>
{% if forloop.counter|divisibleby:"4" %}</tr><tr>{% endif %}
{% endfor %}
</tr>
</table>
HTML is forgiving if you don't have the right number of columns
in your final row (don't know about XHTML's definition). And it
adds a bogus row at the bottom if your dataset is divisible by N.
A better way would be to make a custom template filter that
breaks the data-set into groups.
#####################################
from itertools import izip, islice
def pad(iterable, length, padding=None):
i = 0
for thing in iterable:
i += 1
yield thing
for i in xrange(length - i):
yield padding
def columnize(dataset, args="2"):
if "," in str(args):
columns, padding = args.split(",",1)
else:
columns = args
padding = ""
columns = int(columns)
row_count, leftovers = divmod(len(dataset), columns)
if leftovers: row_count += 1
return izip(*(
pad(islice(dataset, i, None, columns), row_count, padding)
for i in xrange(columns)
))
#####################################
and then register columnize your filter. It should presume 2
columns, but be extensible to more than two columns with an
optional parameter. It also takes optional padding as a
parameter, so it can be called like
<table>
{% for thing in data|columnize:"3, " %}
<tr>
{% for value in thing %}
<td>{{ value }}</td>
{% endfor %}
</tr>
{% endfor %}
</table>
or
<table>
{% for thing in my_queryset|columnize %}
<tr>
{% for value in thing %}
<td>
{% if value %}
<b>{{ value.name }}:</b>
{{ value.other_field }}
{% else %}
{% endif %}
</td>
{% endfor %}
</tr>
{% endfor %}
</table>
which is about as clean a solution as it gets, as it guarantees
"square" result-sets that have been padded out.
-tim