Django 如何按Project模型自定义costs函数对关联项目列表排序
问题原因
Django ORM的order_by属于数据库SQL层面的操作,而你定义的costs()是Python层面的实例方法,数据库无法识别该方法的计算逻辑,因此不能直接用order_by对这个方法的返回值排序,有两种方案可以实现需求:
方案1:Python内存排序(适合关联项目数量少的场景)
直接把查询集转为列表,用Python内置的sorted函数按costs()方法的返回值排序即可,改造非常简单:
class Subprogram(models.Model): title = models.CharField(max_length=100) def projects_sorted(self): # 升序排序,要降序的话添加 reverse=True 参数 # 加 prefetch_related 预加载关联的成本数据,避免N+1查询问题 projects = self.project_set.all().prefetch_related("costs_set") return sorted(projects, key=lambda project: project.costs())
优缺点:改造成本极低,完全复用你已有的costs()方法逻辑;缺点是项目数量多的时候,内存排序和批量计算的性能会明显下降。
方案2:数据库聚合排序(推荐,适合所有场景,性能更高)
直接用Django ORM的聚合功能,在数据库层面直接计算出每个项目的总成本,再完成排序,所有操作都在数据库完成,性能远高于内存排序:
首先在文件头部引入聚合函数:
from django.db.models import Sum
修改projects_sorted方法:
class Subprogram(models.Model): title = models.CharField(max_length=100) def projects_sorted(self): # 升序排序,要降序的话把 order_by 参数改成 '-total_cost' return self.project_set.annotate( total_cost = Sum("costs__cost") ).order_by("total_cost")
说明:Sum("costs__cost")是ORM的跨表聚合写法,对应你costs()方法里的成本求和逻辑,和你自己写的循环累加结果完全一致,不需要再保留Python层面的循环求和逻辑,也可以直接把实例方法改造成用聚合实现,避免重复代码。
内容的提问来源于stack exchange,提问作者user
相关产品推荐
相关产品推荐

