有没有办法通过 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 将我创建的两个字段合并为一个字段,例如将其格式化为字符串。这样做的好处是,您可以使用tuple
s 构造一个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操作:多表查询,聚合查询分组查询F查询Q查询choices参数]