【问题标题】:Django: annotate QuerySet with number of seconds since object createdDjango:使用对象创建后的秒数注释 QuerySet
【发布时间】:2020-03-14 07:13:14
【问题描述】:

我有一个模型,叫它Foo,它有一个属性created_at,它记录了创建实例的时间(UTC)。 我想用自创建返回的每个项目以来的秒数来注释此模型上的查询集。

(更准确地说,我想根据项目的打开时间“打折”项目,并使用可配置的过期时间,但如果我能得到自创建以来的秒数,我可以进行该计算)

我已经尝试了一些显而易见的事情,比如

Foo.objects.annotate(time_since_create=now-F('created_at'))

Foo.objects.annotate(time_since_create=now.timestamp()-F('created_at'))

(其中now isutc.localize(datetime.utcnow())`)

但是这些让我知道AttributeError: 'float' object has no attribute 'tzinfo',这表明日期上的 DB 算术比我想象的要复杂一些。

显然,我可以在python中做这个算术,即,

[(id, now-created_at) for id, created_at in Foo.objects.values_list("id", "created_at")]

但如果我能在查询集中得到它会更好。

【问题讨论】:

  • datetime.now()-F('created_at') 怎么样?您是否有理由要注释查询集?通常渲染数据是模板的职责,不是查询集的职责。
  • @WillemVanOnsem 在我的示例中,nowutc.localize(datetime.utcnow())。编辑问题以明确这一点
  • 但是你为什么使用.timestamp()?然后将其转换为float。如果你使用now - F('created_at')会怎样?
  • @WillemVanOnsem 我都试过了(见帖子中的例子)
  • 在这种情况下,您可能应该测试然后接受下面@Durai 的回答。那应该为你做。显然,如果你想要一个通用的查询集,你需要在最后删除 values() 调用

标签: django date django-queryset


【解决方案1】:

您可以使用的一个解决方案是 django 查询集中的额外解决方案。如果您需要所有记录的差异,则无需执行注释或聚合。

我的数据库是 mysql。所以我在几秒钟内使用了 TIMESTAMPDIFF。 TIMESTAMPDIFF 来自 mysql 数据库。您需要在以下查询集中使用数据库中可用的功能。希望这会有所帮助。

访问https://docs.djangoproject.com/en/2.2/ref/models/querysets/#s-extra

time_since_create = {"time_since_create": """TIMESTAMPDIFF(SECOND, created_at, now())"""}
objs = Foo.objects.extra(select=time_since_create).values('name', 'time_since_create')
str(objs.query)

如果打印 sql 查询,会是这样的。

SELECT (TIMESTAMPDIFF(SECOND, created_at, now())) AS `time_since_create`, `foo`.`name` FROM `foo`

【讨论】:

  • 我认为这是要走的路。如果是我,我会在最后去掉“values()”部分,我会使用更精确的名称,因为我们说的是整数(秒)而不是抽象(时间)。所以,“seconds_since_created”更合适恕我直言
【解决方案2】:

第一种方法

你可以像这样过滤你的查询集

time_threshold =  datetime.now() - timedelta(munutes=40)
Foo.objects.filter(created_at__lte=time_threshold)

Source

输出

> <QuerySet [<Foo: 2019-11-18 20:56:16.228271+00:00>, <Foo: 2019-11-18
> 20:56:31.842079+00:00>, <Foo: 2019-11-18 20:56:32.761461+00:00>, <Foo:
> 2019-11-18 20:57:04.612091+00:00>, <Foo: 20 19-11-18
> 20:57:11.401817+00:00>, <Foo: 2019-11-18 21:00:12.794631+00:00>, <Foo:
> 2019-11-18 21:00:14.935930+00:00>, <Foo: 2019-11-18
> 21:00:59.107677+00:00>, <Foo: 2019-11-18 21:01:27.5 90956+00:00>]>

第二种方法

您可以在模型中创建一个 def 来计算自创建以来经过的时间 然后使用它

class Foo(models.Model):
    created_at = models.DateTimeField(auto_now_add=True)

    def time_elapsed_since_create(self):
        return datetime.now(timezone.utc) - self.created_at

    def __str__(self):
        return str(self.created_at)

输出

>>> bar = Foo.objects.create()
>>> print(bar.time_elapsed_since_create())
0:00:09.480279
>>> print(bar.time_elapsed_since_create())
0:00:10.560474
>>> print(bar.time_elapsed_since_create())
0:00:11.248255

>>> print(type(bar.time_elapsed_since_create()))
<class 'datetime.timedelta'>

获取所有超过 60 秒的 Foo 的功能

>>> for i in Foo.objects.all():
...     if i.time_elapsed_since_create() > timedelta(seconds=60):
...             print(i.id, i.created_at, i.time_elapsed_since_create())
...
1 2019-11-18 20:56:16.228271+00:00 0:13:12.512974
2 2019-11-18 20:56:31.842079+00:00 0:12:56.899166
3 2019-11-18 20:56:32.761461+00:00 0:12:55.979784
4 2019-11-18 20:57:04.612091+00:00 0:12:24.129154
5 2019-11-18 20:57:11.401817+00:00 0:12:17.340419
6 2019-11-18 21:00:12.794631+00:00 0:09:15.947605
7 2019-11-18 21:00:14.935930+00:00 0:09:13.806306
8 2019-11-18 21:00:59.107677+00:00 0:08:29.634559
9 2019-11-18 21:01:27.590956+00:00 0:08:01.152280

【讨论】:

  • 很遗憾,这无助于我在查询集中返回它。
  • @JonKiparsky 检查第一种方法刚刚更新了代码
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2017-11-30
  • 2018-06-09
  • 2011-10-24
  • 2018-12-30
  • 2019-01-22
  • 2016-01-07
  • 1970-01-01
相关资源
最近更新 更多