views:

399

answers:

4

My question is almost exactly the same as this post, except that I'm using Python and Django instead of PHP.

The task is to take:

id date  
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

And output:

2009-01-01  
1  
2   
2009-01-02  
3  
2009-01-03  
4  
5

I can manually hack something together by looping through the sorted dates and outputting HTML into a string in my python view file, but I'm wondering: is there's a better way using a Django template or a nifty Python feature?

+2  A: 

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.

Dana the Sane
I can group the data, but what would the "little logic" inside the templating system look like?
amdfan
I added an example.
Dana the Sane
I'm not convinced you can do this in Django templates. Even if you could, you wouldn't want to. It's better to do it in:(1) the view code or (2) a templatetag where you can use real python.
hughdbrown
I consider templates to be part of the view in the MVC model. I think that whether you decide to do any data formatting in your views (rather than the controller) depends on the situation, but it's necessary sometimes.
Dana the Sane
Thanks for the answer, but I would really prefer to do this in a view function using Python. Django's templating system isn't designed to handle this type of stuff.
amdfan
If you want to use a view function (not in the template, as originally described: "is there's a better way using a Django template or a nifty Python feature?"), then Dana the Sane's code is on the right track, though I think that the itertools.groupby() solution is more complete and pythonic.
hughdbrown
+5  A: 

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')
Triptych
Excellent! itertools.groupby is just what I was looking for.
amdfan
If you are going to do it in the view, you want something like this:"def sidebar_date_list():from itertools import groupbydata = [datetime.date(post.created_at.year, post.created_at.month, 1) for post in Post.objects.filter(publish=1).order_by('-created_at') ]return {'months': [k for k,_ in groupby(data)]}Notice that the SQL has to be sorted for groupby() to use." Lifted from here:http://www.djangrrl.com/view/using-python-str-datetime-lists-and-sets-to-group-dates/
hughdbrown
Or like this if you want to group by days: "def sidebar_date_list(): from itertools import groupby data = [datetime.date(post.created_at.year, post.created_at.month, post.created_at.day) for post in Post.objects.filter(publish=1).order_by('-created_at') ] return {'days': [k for k,_ in groupby(data)]}"
hughdbrown
Wade's method below, for doing this in the template worked better of me. Added bonus was that I could use the date-based generic views and get rid of some code.
saturdayplace
+3  A: 

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.

hughdbrown
Templating languages tend to provide enough of the same language constructs that you can convert between the two easily (they share the same target bytecode).
Dana the Sane
Not to be rude, but do you program in Django? For practical purposes, there are only rudimentary comparisons and assignment operations. Check out what is available without using templatetags:http://docs.djangoproject.com/en/dev/topics/templates/
hughdbrown
Unfortunately, I'm using 0.96 but this is a great resource for those on 1.0 or greater!
amdfan
It turns out that regroup is available in 0.96. http://www.djangoproject.com/documentation/0.96/templates/I didn't claim that originally because I had not programmed in 0.96 and had no inkling that you were using it. Give it a try if you still want to do the transformation in the template.
hughdbrown
+3  A: 

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
I was most of the way there but didn't realize you could mix in the date filter. Many thanks for this! Exactly what I was looking for.
saturdayplace