不幸的是,没有办法(我知道..我看起来很努力)避免使用某种原始 sql 来完成您想要做的事情(使用您当前的模型;请参阅最后的另一个建议)。但是您可以通过编写尽可能少的原始 sql 来最小化影响。在实践中,django 站点不需要跨不同的数据库移植。除非您打算在其他地方使用此应用程序或公开发布它,否则您应该没问题。
以下示例适用于 sqlite。您可以保留数据库类型到date
函数的映射,查找驱动程序的类型,并在需要时将函数替换为正确的函数。
>>> for stat in Stats.objects.all():
... print stat.created, stat.growth
...
2013-06-22 13:41:25.334262+00:00 3
2013-06-22 13:41:40.473373+00:00 3
2013-06-22 13:41:44.921247+00:00 4
2013-06-22 13:41:47.533102+00:00 5
2013-06-23 13:41:58.458250+00:00 6
2013-06-23 13:42:01.282702+00:00 3
2013-06-23 13:42:03.633236+00:00 1
>>> last_stat_per_day = Stats.objects.extra(
select={'the_date': 'date(created)' }
).values_list('the_date').annotate(max_date=Max('created'))
>>> last_stat_per_day
[(u'2013-06-22', datetime.datetime(2013, 6, 22, 13, 41, 47, 533102, tzinfo=<UTC>)), (u'2013-06-23', datetime.datetime(2013, 6, 23, 13, 42, 3, 633236, tzinfo=<UTC>))]
>>> max_dates = [item[1] for item in last_stat_per_day]
>>> max_dates
[datetime.datetime(2013, 6, 22, 13, 41, 47, 533102, tzinfo=<UTC>),
datetime.datetime(2013, 6, 23, 13, 42, 3, 633236, tzinfo=<UTC>)]
>>> stats = Stats.objects.filter(created__in=max_dates)
>>> for stat in stats:
... print stat.created, stat.growth
...
2013-06-22 13:41:47.533102+00:00 5
2013-06-23 13:42:03.633236+00:00 1
我之前在这里写过,这只是一个查询,但我撒了谎 - values_list 需要转换为只返回后续查询的 max_date,这意味着运行语句。虽然它只有 2 个查询,但比 N+1 函数要好得多。
非便携式位是这样的:
last_stat_per_day = Stats.objects.extra(
select={'the_date': 'date(created)' }
).values_list('the_date').annotate(max_date=Max('created'))
使用extra
并不理想,但这里的原始 sql 很简单,并且非常适合依赖于数据库驱动程序的替换。只有date(created)
需要更换的。如果愿意,您可以将其包装在自定义管理器上的方法中,然后您已成功地将这些混乱抽象到一个位置。
另一种选择是只将 a 添加DateField
到您的模型中,然后您根本不需要使用额外的。您只需将values_list
呼叫替换为 a values_list('created_date')
,完全删除extra
,然后就可以结束了。成本是显而易见的——需要更多的存储空间。至于为什么你在同一个模型上有一个Date
和一个DateTime
字段也是不直观的。保持两者同步也可能会带来问题。