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
值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)