每天使用.latest()的QuerySet

Jan*_*nis 9 django

我有一个基本的模型,如:

class Stats(models.Model):

   created = models.DateTimeField(auto_now_add=True)
   growth = models.IntegerField()
Run Code Online (Sandbox Code Playgroud)

我每10分钟运行一次芹菜作业来创建一个新的统计对象.

使用.latest()QuerySet给了我最新的统计对象日期.

但是,我想要一个每天都有一个Stats对象的列表.

考虑以下:

Stats(growth=100) #created 1/1/13 23:50
Stats(growth=200) #created 1/1/13 23:59
Stats(growth=111) #created 1/2/13 23:50
Stats(growth=222) #created 1/2/13 23:59
Run Code Online (Sandbox Code Playgroud)

QuerySet应返回最新的每一天.在示例中,具有200和222增长的那个.

在SQL中,我每天都会使用max来启动一个子查询并将它们连接在一起.

由于我不想使用原始SQL,有没有办法用django ORM做到这一点?

Jos*_*ton 4

不幸的是,没有办法(我知道......我看起来很努力)来避免使用某种原始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
Run Code Online (Sandbox Code Playgroud)

我之前曾在这里写过,这只是一个查询,但我撒了谎——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'))
Run Code Online (Sandbox Code Playgroud)

使用extra并不理想,但这里的原始 sql 很简单,并且非常适合依赖于数据库驱动程序的替换。仅date(created)需要更换。如果您愿意,您可以将其包装在自定义管理器的方法中,然后您就可以成功地将这些混乱抽象到一个位置。

另一种选择是仅将 a 添加DateField到您的模型中,然后您根本不需要使用额外的。您只需将调用替换values_listvalues_list('created_date'),完全删除extra,然后就到此为止了。成本是显而易见的——需要更多的存储空间。为什么同一模型上有 aDate和 a字段也是不直观的。DateTime保持两者同步也可能会带来问题。