有没有办法通过 Django ORM 查询修改日期时间对象?

Posted

技术标签:

【中文标题】有没有办法通过 Django ORM 查询修改日期时间对象?【英文标题】:Is there a way to modify datetime objects through the Django ORM Query? 【发布时间】:2016-11-11 07:20:39 【问题描述】:

我们有一个 Django,Postgresql 数据库,其中包含以下对象:

object_date = models.DateTimeField()

作为一个字段。

我们需要每天按小时统计对象,所以我们需要去掉一些额外的时间数据,例如:分钟、秒和微秒。

我们可以去掉python中多余的时间数据:

query = MyModel.objects.values('object_date')
data = [tweet['tweet_date'].replace(minute=0, second=0, microsecond=0) for tweet in query

这给我们留下了一个包含日期和时间的列表。

我的问题:在查询本身中是否有更好、更快、更简洁的方法来执行此操作?

【问题讨论】:

这可能会有所帮助,看起来有人在做类似的事情:***.com/questions/30270371/… 【参考方案1】:

如果你只是想获取没有时间数据的日期,可以使用extra声明计算字段:

query = MyModel.objects
    .extra(select=
        'object_date_group': 'CAST(object_date AS DATE)',
        'object_hour_group': 'EXTRACT(HOUR FROM object_date)'
    )
    .values('object_date_group', 'object_hour_group')

不过,您不会从中获得太多收益;数据库现在正在向您发送更多数据。

但是,使用这些附加字段,您可以使用聚合来立即获取您要查找的计数,只需添加一行:

query = MyModel.objects
    .extra(select=
        'object_date_group': 'CAST(object_date AS DATE)',
        'object_hour_group': 'EXTRACT(HOUR FROM object_date)'
    )
    .values('object_date_group', 'object_hour_group')
    .annotate(count=Count('*'))

或者,您可以使用任何有效的 SQL 将我创建的两个字段合并为一个字段,例如将其格式化为字符串。这样做的好处是,您可以使用tuples 构造一个Counter 以方便查询(使用values_list())。

这个查询肯定会比在 Python 中进行计数更有效。但是,对于可能不那么重要的后台作业。

一个缺点是此代码不可移植;一方面,它不适用于 SQLite,您可能仍将其用于测试目的。在这种情况下,您可能会省去麻烦并立即编写 raw 查询,这将同样不可移植但更具可读性。

更新

由于添加了TruncHour,从 1.10 开始,可以使用 expressions 很好地执行此查询。以下是解决方案外观的建议:

from collections import Counter
from django.db.models import Count
from django.db.models.functions import TruncHour

counts_by_group = Counter(dict(
    MyModel.objects
        .annotate(object_group=TruncHour('object_date'))
        .values_list('object_group')
        .annotate(count=Count('object_group'))
)) # query with counts_by_group[datetime.datetime(year, month, day, hour)]

它优雅、高效且便携。 :)

【讨论】:

看起来不错,但我需要计算每天的小时数,所以我需要日期和小时。主要是好奇,如果在查询中以某种方式执行此操作,这是否会比在 Celery 任务或视图和模板中的 vanilla python 中执行此操作更快。 @a_Fraley 答案大大改善。 :) 第一段代码有效,因为即使在测试中我也使用 Postgresql 数据库。我要试试这第二组。我正在阅读 1.10 文档。有趣的。 :) 谢谢。【参考方案2】:
count = len(MyModel.objects.filter(object_date__range=(beginning_of_hour, end_of_hour)))

count = MyModel.objects.filter(object_date__range=(beginning_of_hour, end_of_hour)).count()

假设我了解您的要求,这将返回在特定时间范围内具有日期的对象的数量。将范围设置为从一小时的开始到一小时的结束,您将返回该小时内创建的所有对象。 Count()len() 可根据所需用途使用。欲了解更多信息,请查看https://docs.djangoproject.com/en/1.9/ref/models/querysets/#count

【讨论】:

这似乎接近我们正在寻找的。我们有 9 天的对象,按小时计算 17,685 个对象,所以我想从 DateTimeField() 中删除除日期本身和小时之外的所有内容,然后使用查询 count() 在数据库本身中计算这些对象;这项任务需要尽可能快。 如果您想在一个特定的小时内执行此操作,而不是使用 .count() 进行分组和注释,hour 过滤器也可以使用 - docs.djangoproject.com/en/1.9/ref/models/querysets/#hour。尽管将其限制在正确的日期将需要另一个过滤子句(因此,您要计算的是某一天的上午 9 点到 10 点,而不是所有天),因此这两个选项之间没有太大的实际差异。 对不起,我的回答应该说过滤不是我之前编辑过的值。无论如何,我真的不明白剥离数据将如何帮助提高速度。 count() 可以替换 len(),这取决于您使用它的目的。对于更多优化技巧,可能有一种更快的方法来过滤和缓存您的查询,以使它们随着您的继续变得越来越小,因此我建议您也对此进行一些研究

以上是关于有没有办法通过 Django ORM 查询修改日期时间对象?的主要内容,如果未能解决你的问题,请参考以下文章

有没有办法跟踪Django ORM中的事务更改?

通过 orm 的 django 子查询

基于比较日期的Django查询集[重复]

[Django框架之ORM操作:多表查询,聚合查询分组查询F查询Q查询choices参数]

如何在 Django ORM 中更改 PostgreSQL 的默认空排序行为

Django ORM,通过查询求和列值