Python Django:使用排列数据优化查询

Python Django:使用排列数据优化查询,python,django,django-models,django-queryset,Python,Django,Django Models,Django Queryset,我有Order对象和OrderOperation对象,它们表示订单上的操作(创建、修改、取消) 从概念上讲,一个订单有1到多个订单操作。每次对订单进行操作时,都会在此操作中计算总数。这意味着当我需要求一个订单的总数时,我只需要求最后一个订单操作的总数 简化代码 问题 因为我收到了很多订单,每次我想做一个简单的筛选,比如“订单总数低于5欧元”,这需要很长时间,因为我需要浏览所有订单,使用以下明显不好的查询: all_objects = Order.objects.all() Order.objec

我有
Order
对象和
OrderOperation
对象,它们表示订单上的操作(创建、修改、取消)

从概念上讲,一个订单有1到多个订单操作。每次对订单进行操作时,都会在此操作中计算总数。这意味着当我需要求一个订单的总数时,我只需要求最后一个订单操作的总数

简化代码 问题 因为我收到了很多订单,每次我想做一个简单的筛选,比如“订单总数低于5欧元”,这需要很长时间,因为我需要浏览所有订单,使用以下明显不好的查询:

all_objects = Order.objects.all()
Order.objects.prefetch_related('orderoperation_set').filter(
    pk__in=[o.pk for o in all_objects if o.total <= some_value])
这是非常快,但过滤是坏的,因为我没有过滤最后的操作,但所有的操作在这里。这是错误的

还有别的想法吗?谢谢。

使用annotate()方法 看来这是不可能的

当然,这是可能的;)您可以使用子查询或一些巧妙的条件表达式。假设您希望从上次订单操作中获取总金额,下面是子查询的示例:

from django.db.models import Subquery, OuterRef

orders = Order.objects.annotate(
    total=Subquery(                             # [1]
        OrderOperation.objects \
            .filter(order_id=OuterRef("pk")) \  # [2]
            .order_by('-id') \                  # [3]
            .values('total') \                  # [4]
            [:1]                                # [5]
    )
)
以上代码说明:

  • 我们正在将新字段添加到结果列表中,名为
    total
    taht的字段将由子查询填充。您可以在此查询集中作为模型
    顺序
    的任何其他字段访问它(在对其求值之后,在模型实例中,或在筛选和其他注释中)。您可以从中了解注释的工作原理
  • 子查询只能为当前顺序中的操作调用
    OuterRef
    将被替换为结果SQL查询中对所选字段的引用
  • 我们想按操作
    id
    降序订购,因为我们确实需要最新的。如果您的操作中有其他字段(如创建日期)需要按其排序,请在此处填写
  • 该子查询应该只返回操作中的
    total
  • 我们只需要一个元素。它是使用切片表示法而不是普通索引获取的,因为在django QuerySet上使用索引将立即调用它。切片只在SQL查询中添加
    LIMIT
    子句,而不调用它,这就是我们想要的
  • 现在您可以使用:

    orders.filter(total__lte=some_value)
    

    只取你想要的订单。您也可以使用该注释

    以后是否需要使用
    Order
    对象?我可以建议使用
    values()
    作为:
    OrderOperation.objects.filter(total\uu lte=some\u value)。value('order')
    。或者在
    值上指定所需的
    顺序
    字段
    @SergeyPugach感谢
    的想法,听起来不错。但是,通过这种方式,它将获得所有的
    订单操作
    ,而我需要为每个
    订单
    获取最后一个操作。例如,如果订单被修改,它有2个操作:1个创建,然后1个修改。在这种情况下,正确的总数就是修改(最后一次操作)中的总数。哇,令人惊讶的答案,非常感谢!但是,当尝试使用
    订单
    时(我猜在计算查询集时),我得到了一个
    属性错误:无法设置属性
    错误。有什么想法吗?我想注释与模型中定义的属性
    total
    冲突。重命名批注或属性。你让我开心了。你能看一下吗?这是密切相关的。再次感谢。
    from django.db.models import Subquery, OuterRef
    
    orders = Order.objects.annotate(
        total=Subquery(                             # [1]
            OrderOperation.objects \
                .filter(order_id=OuterRef("pk")) \  # [2]
                .order_by('-id') \                  # [3]
                .values('total') \                  # [4]
                [:1]                                # [5]
        )
    )
    
    orders.filter(total__lte=some_value)