在Django中对日期进行分组

18

我的问题与这篇帖子几乎完全相同,除了我使用的是Python和Django而不是PHP。

任务是:

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 

输出结果为:

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

我可以通过在Python视图文件中循环遍历已排序日期并将HTML输出到字符串中来手动地实现一些东西,但是我想知道:是否有更好的方法可以使用Django模板或巧妙的Python特性?

5个回答

41

你可以使用 regroupdate 模板标签来解决这个问题。

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

我已经做得差不多了,但没有意识到可以混合使用日期过滤器。非常感谢您!这正是我在寻找的。 - saturdayplace
天啊,我怎么不知道这个。 - Timmy O'Mahony
如果您需要“日期部分”,使用start_date.date会更快。 - Riccardo Galli
1
如果性能是一个问题,可以考虑我的答案https://dev59.com/rXM_5IYBdhLWcg3ww2Kb#8597940。 - tback
这个答案太棒了! - Juan Diego Ramirez

16

itertools.groupby 是您亲爱的好朋友:

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

以上代码输出如下:

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')

太好了!itertools.groupby 正是我正在寻找的。 - ine
如果您要在视图中执行此操作,您需要像这样编写代码:"def sidebar_date_list(): from itertools import groupby data = [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)]}请注意,SQL 必须按照 groupby() 函数的要求进行排序。" 参考链接: http://www.djangrrl.com/view/using-python-str-datetime-lists-and-sets-to-group-dates/ - hughdbrown
如果您想按天分组,可以这样做:"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的方法适用于我在模板中完成此操作。额外的好处是,我可以使用基于日期的通用视图并且不需要一些代码。 - saturdayplace

9

在处理较大的数据集时,itertools.group_by 可能会过慢。在这些情况下,我让Postgres处理分组:

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')

8
如果OP可以在模板代码中使用Python,那么这些都是合理的答案。(在这种情况下,我会支持itertools.groupby()的解决方案。) 但是你不能在模板代码中使用Python。在最好的情况下,你需要编写一个templatetag来执行此操作。而且已经有一个templatetag可以做到这一点。要在Django模板中进行分组,请使用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>

这是从Django regroup文档中提取的示例。regroup在django 0.961.01.1中都可用。

如果您告诉我传递给模板的内容,我可以编写一个使用您数据的示例。

此外,有一篇博客文章详细介绍了DjangoGrrl如何解决这个问题。


模板语言往往提供足够的相同语言结构,使您可以轻松地在两者之间进行转换(它们共享相同的目标字节码)。 - Dana the Sane
不想冒犯,但你会用Django编程吗?就实际目的而言,只有基本的比较和赋值操作。请查看在不使用模板标签的情况下可用的内容:http://docs.djangoproject.com/en/dev/topics/templates/ - hughdbrown
很遗憾,我正在使用0.96版本,但这对于那些使用1.0或更高版本的人来说是一个很好的资源! - ine
1
原来在0.96版本中是有regroup功能的。http://www.djangoproject.com/documentation/0.96/templates/ 我之前没有提到这点是因为我没有使用过0.96版本,也不知道你在使用它。如果你仍然想在模板中进行转换,请尝试一下。 - hughdbrown

2

如果你使用数据库的日期格式化函数来去除时间(在select语句中),那么这将更容易。然后,你可以使用列别名对格式化的列进行分组。

之后,你可以使用一个循环和一些逻辑将结果转换为所需的格式。我还没有使用过django的模板系统,但以下是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

应该有一种简单明了的方法将此转换为Django模板代码。


我可以对数据进行分组,但模板系统中的“小逻辑”应该是什么样子的? - ine
我不确定你能否在Django模板中完成这个任务。即使你可以,也不建议这样做。最好在视图代码或者模板标签中使用真正的Python来完成它。 - hughdbrown
我认为模板是MVC模型中视图的一部分。我认为在视图中决定是否进行数据格式化(而不是控制器)取决于具体情况,但有时候这是必要的。 - Dana the Sane
谢谢你的回答,但我真的更喜欢使用Python中的视图函数来完成这个任务。Django的模板系统并不适合处理这种类型的内容。 - ine
如果你想要使用视图函数(而不是在模板中,如最初描述的:“是否有一种更好的方法使用Django模板或巧妙的Python功能?”),那么Dana the Sane的代码是正确的方向,尽管我认为itertools.groupby()解决方案更完整和符合Python编程规范。 - hughdbrown

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接