与 django 查询集注释中的先前对象的区别

Posted

技术标签:

【中文标题】与 django 查询集注释中的先前对象的区别【英文标题】:Difference with previous object in django queryset annotation 【发布时间】:2018-09-13 14:36:25 【问题描述】:

假设我有以下模型:

class TestModel(models.Model):
    some_integer = models.IntegerField()

我有这个模型的 3 个实例:

TestModel.objects.create(some_integer=100)
TestModel.objects.create(some_integer=50)
TestModel.objects.create(some_integer=20)

我想以某种方式对查询集进行注释以便能够获得以下结果:

for obj in TestModel.objects.annotate(difference=...):
    print(obj.difference)
=> 50  # 100 - 50
=> 30  # 50 - 30
=> None  # we don't have anything created after this record

是否有机会使用 Django(2.0) 查询集执行此操作,或者我应该在 Python 中“手动”执行此操作?我们可以放心地假设对象是按 pk 或某个日期排序的,因此保留了排序。

【问题讨论】:

我认为概念的问题是标准的关系数据库没有上一个或下一个的概念。元素可以按任何顺序返回,除非元素是明确排序的。 @WillemVanOnsem 我们可以假设 QuerySet 是按 pk 或某个日期排序的,我已经用这些信息更新了描述。 【参考方案1】:

基于这个Answer,如果你想使用原始SQL,你可以这样做:

SELECT *,
       LEAD("some_integer") OVER(ORDER BY "id") AS "next_val",
       "some_integer" - "next_val" AS "difference"
FROM "myapp_testmodel";

但是,从 Django 版本 2.0 开始,您可以使用 Window Functions 创建上述查询:

from django.db.models import Window, F
from django.db.models.functions import Lead

q = TestModel.objects.annotate(
    next_val=Window(
        expression=Lead('some_integer', offset=1, default=0),
        order_by=F('id').asc()
    ), 
    difference=F('some_integer')-F('next_val')
)

注意:我已经使用0作为默认值(将应用于最后一项),以简化答案以防止从Integer中减去Null值引起FieldError。您可以使用 Django 的 Case 查询为最后一项返回 None 。

【讨论】:

谢谢!我从来没有花时间看窗口函数,但我想这会发生在现在:) 如果我只是注释 next_val 它工作正常,但如果我在另一个注释中使用 next_val difference 它会抛出错误:window functions are not allowed in GROUP BY。我在这里错过了什么?

以上是关于与 django 查询集注释中的先前对象的区别的主要内容,如果未能解决你的问题,请参考以下文章

Django:在请求值时从查询集注释字段中删除小数前缀

Django 根据先前的查询选择数据库对象

用 Django ORM 中的前一个对象注释查询集

在 django 中,有没有办法在单个查询中直接用相关对象注释查询?

Django 查询 - 在带注释的计数过滤器中获取父对象

(转)解决swagger跨项目或跨程序集注释不显示问题