[Fixed]-Grouping dates in Django

41👍

You can solve this with the regroup and date templatetags.

{% regroup object_list by start_date|date:"Y-m-d" as objects_by_day %}
{% for d in objects_by_day %}
### DO SOMETHING HERE
{% endfor %}
👤Wade

16👍

itertools.groupby is your dear, dear friend:

import itertools

dates = [
   (1,'2009-01-01 10:15:23'),
   (2,'2009-01-01 13:21:29'),
   (3,'2009-01-02 01:03:13'),
   (4,'2009-01-03 12:20:19'),
   (5,'2009-01-03 13:01:06'),
]

for key,group in itertools.groupby(dates, key=lambda x: x[1][:11]):
   print key
   for element in group:
      print '   ', element

The above code prints the following:

2009-01-01 
    (1, '2009-01-01 10:15:23')
    (2, '2009-01-01 13:21:29')
2009-01-02 
    (3, '2009-01-02 01:03:13')
2009-01-03 
    (4, '2009-01-03 12:20:19')
    (5, '2009-01-03 13:01:06')

9👍

When doing reports on larger datasets itertools.group_by might be too slow. In those cases I make postgres handle the grouping:

truncate_date = connection.ops.date_trunc_sql('day','timestamp')
qs = qs.extra({'date':truncate_date})
return qs.values('date').annotate(Sum('amount')).order_by('date')
👤tback

8👍

These are all reasonable answers if the OP could use python in the template code. (And on that account, I would favor the itertools.groupby() solution.) But you can’t use python in template code. In the best case, you’d write a templatetag that performed this operation. And there is already a templatetag that does this.

To do the grouping in Django inside the template, use regroup:

{% regroup people by gender as gender_list %}

<ul>
{% for gender in gender_list %}
    <li>{{ gender.grouper }}
    <ul>
        {% for item in gender.list %}
        <li>{{ item.first_name }} {{ item.last_name }}</li>
        {% endfor %}
    </ul>
    </li>
{% endfor %}
</ul>

Example lifted from Django regroup documentation. regroup is available in django 0.96, 1.0, and 1.1, at least.

If you gave me an idea of what is passed through to the template, I could hack up an example that uses your data.

Also, there is a blog post in which DjangoGrrl works through exactly this question.

2👍

This would be easier if you used your database’s date formatting functions to strip off the times (in the select). You can then group by the formatted column (using a column alias).

After that, you can use a loop with a little logic to transpose the results to what you want. I haven’t used django’s template system yet, but here’s what the logic would look like in Python:

>>> rows = [(1,'2009-01-01'), (2,'2009-01-01'), (3, '2009-02-02'), \
(4, '2009-02-02'), (5, '2009-02-02')]
>>> topdate = None
>>> for r in rows:
...     if topdate <> r[1]:
...             topdate = r[1]
...             print r[1]
...     print r[0]
... 
2009-01-01
1
2
2009-02-02
3
4
5

There should be a straight-forward way to convert this to django template code.

Leave a comment