如何在Django中高效统计模型外键关联的实例总数?
问题原因
你之前的优化方案结果不对,核心原因是Count('house', distinct=True)统计的是单条Fetch记录关联的House对象数量,因为一条Fetch只关联一个House,所以结果永远是1,完全没有统计该House下所有关联Fetch的总数。
解决方案
方案1:直接在Fetch查询层添加关联计数注解(最适合当前场景)
只需要修改你的QuerySet注解逻辑,通过双下划线跨关联关系统计对应House、Person下的所有Fetch总数即可,整个查询只会执行1次SQL,完全解决N+1问题:
from django.db.models import Count class HomeView(ListView): queryset = Fetch.objects.select_related( 'house', 'person' ).annotate( # 统计当前Fetch关联的House下所有Fetch总数 house_fetch_total=Count('house__fetchs', distinct=True), # 统计当前Fetch关联的Person下所有Fetch总数 person_fetch_total=Count('person__person_fetchs', distinct=True) ) template_name = "index.html" paginate_by = 100
模板层直接调用注解字段即可,不需要再调用模型的自定义计数方法:
{% for obj in object_list %} <tr> <td>{{ obj.company.name }}</td> <td>{{ obj.house_fetch_total }}</td> <td>{{ obj.person_fetch_total }}</td> </tr> {% endfor %}
方案2:全局复用计数逻辑(适合多个视图都需要用到该计数的场景)
如果你的项目中多处需要获取House、Person的关联Fetch总数,可以把计数逻辑封装到模型的查询管理器中,避免重复写注解:
- 给House和Person模型添加自定义管理器
from django.db import models from django.db.models import Count class HouseManager(models.Manager): def get_queryset(self): return super().get_queryset().annotate( fetch_total=Count('fetchs', distinct=True) ) class House(models.Model): name = models.CharField(max_length=150) objects = HouseManager() class PersonManager(models.Manager): def get_queryset(self): return super().get_queryset().annotate( fetch_total=Count('person_fetchs', distinct=True) ) class Person(models.Model): house = models.ForeignKey(House, related_name="persons", on_delete=models.PROTECT) first_name = models.CharField(max_length=150) objects = PersonManager()
- 之后无论从哪里查询House/Person对象,都可以直接调用
fetch_total属性获取计数,Fetch查询只需要保留select_related即可:
queryset = Fetch.objects.select_related('house', 'person')
模板调用:
<td>{{ obj.house.fetch_total }}</td> <td>{{ obj.person.fetch_total }}</td>
注意:你给出的Fetch模型代码里有笔误,
house = models.ForeignKey(Hourse, related_name="fetchs")中的Hourse应该是House,请先修正避免运行报错。
内容的提问来源于stack exchange,提问作者PercySherlock
相关产品推荐
相关产品推荐

