Django如何按非关联表聚合值对查询结果执行order_by排序
解决方案
你之前用@property定义的属性是Python运行时计算的,Django ORM的order_by是在数据库层面执行SQL实现排序,无法识别该类属性,你可以通过ORM注解(annotate)在查询时将负债金额计算逻辑下沉到数据库层面,即可支持排序:
方法1:直接在查询时注解(推荐)
首先导入需要的ORM工具函数:
from django.db.models import Sum, OuterRef, Subquery, Coalesce
构造查询语句:
# 构造Accrual表的聚合子查询,按register_no匹配对应金额总和 accrual_debt = Accrual.objects.filter( register_no=OuterRef("register_no") ).values("register_no").annotate( total=Sum("amount") ).values("total") # 查询Member时注解debt字段,无负债默认值设为0,直接按debt排序 members = Member.objects.annotate( debt=Coalesce(Subquery(accrual_debt), 0) ).order_by("debt") # 如需降序排序改为.order_by("-debt")
执行上述查询后得到的Member实例会自带debt属性,和你之前@property定义的取值完全一致,同时支持ORM的排序、过滤等操作。
Driver模型的查询逻辑完全相同,只需将上述代码中的Member替换为Driver即可。
方法2:封装为自定义管理器(适合高频使用场景)
如果该查询逻辑使用频率很高,可以封装到模型的自定义管理器中,避免重复写代码:
from django.db import models from django.db.models import Sum, OuterRef, Subquery, Coalesce class MemberManager(models.Manager): def with_debt(self): accrual_debt = Accrual.objects.filter( register_no=OuterRef("register_no") ).values("register_no").annotate( total=Sum("amount") ).values("total") return self.annotate( debt=Coalesce(Subquery(accrual_debt), 0) ) class Member(models.Model): register_no = models.PositiveIntegerField(unique=True, verbose_name=_('Register No')) # 绑定自定义管理器 objects = MemberManager()
后续使用时直接调用即可:
# 直接获取按负债升序排序的成员列表 members = Member.objects.with_debt().order_by("debt")
小数据量可选方案:Python层面排序
如果你的数据集非常小,也可以直接在内存中排序,但该方案会产生N+1查询问题,数据量较大时性能极差,不推荐使用:
members = sorted(Member.objects.all(), key=lambda x: x.debt)
内容的提问来源于stack exchange,提问作者erhan
相关产品推荐
相关产品推荐

