首页 > 解决方案 > 如何在我的 Django ORM 查询中将 timedelta 与列一起使用?

问题描述

我正在使用 Django 和 Python 3.7。我有以下两种型号...

class Article(models.Model):
    ...
    publisher = models.ForeignKey(Publisher, on_delete=models.CASCADE, related_name="articles",)
    created_on = models.DateTimeField(default=datetime.now)

class WebPageStat(models.Model):
    ...
    publisher = models.ForeignKey(Publisher, on_delete=models.CASCADE, related_name="stats", )

    elapsed_time_in_seconds = models.FloatField(default=0)
    score = models.BigIntegerField(default=0)

class Publisher(models.Model):
   name = models.CharField(max_length=100)

   def __str__(self):
       return self.name

我想编写一个 Django ORM 查询,在给定发布者和以秒为单位的经过时间(WebPageStat 记录)的情况下,我找到所有“created_on”日期不早于以秒为单位的经过时间的文章。许多人建议在其他帖子中使用“timedelta”,但这似乎对我不起作用......

Article.objects.filter(created_on__lte=datetime.now(timezone.utc) - timedelta(hours=0, minutes=0, seconds=publisher__stats__elapsed_time_in_seconds))
Traceback (most recent call last):
  File "<input>", line 1, in <module>
NameError: name 'publisher__stats__elapsed_time_in_seconds' is not defined

我可以将 timedelta 与 SQL 列逻辑一起使用吗?否则我该怎么做?

标签: pythonsqldjangopython-3.xorm

解决方案


这里的根本问题是你不明白为什么你会收到错误

name 'publisher__stats__elapsed_time_in_seconds' is not defined`.

我们再看一下代码:

Article.objects.filter(created_on__lte=datetime.now(timezone.utc) -
    timedelta(hours=0, minutes=0, seconds=publisher__stats__elapsed_time_in_seconds))

在上面的代码中,符号publisher__stats__elapsed_time_in_seconds被解释为变量引用,这在您的代码中没有定义。如果您添加了

publisher__stats__elapsed_time_in_seconds = 1

就在上面的代码片段之前,那么您将不会收到错误消息。(你也不会得到你想要的结果。)你期望 Django 的 ORM 对符号进行操作,publisher__stats__elapsed_time_in_seconds但在 Django 可以得到它之前,Python 解释器必须解释代码,以及代码的编写方式,这只是一个解释器必须解析的变量名。Django 甚至没有机会看到它。

好的,防止解释器将名称解释为变量引用并让 Django ORM 处理名称的方法是使用F()表达式。所以你很想这样做:

Article.objects.filter(created_on__lte=datetime.now(timezone.utc) -
    timedelta(hours=0, minutes=0, seconds=F("publisher__stats__elapsed_time_in_seconds")))

但是随后您将传递给timedelta它不知道如何处理的参数。

正如 Schillingt 在评论中指出的那样,Lutz Prechelt在其他地方的回答显示了如何将F()表达式移到timedelta. 在你的情况下,你可以这样做:

Article.objects.filter(created_on__lte=datetime.now(timezone.utc) -
        timedelta(seconds=1) * F("publisher__stats__elapsed_time_in_seconds"))))

推荐阅读