Django QuerySet带filter过滤时Sum聚合返回None而非0问题
问题根因
- 你把
jlg1__puntos__etapa=2这类关联表过滤条件写在了子查询最外层的filter上,执行时会先把没有对应赛段积分记录的队伍直接从子查询结果集中排除 - 你写在annotate里的
Coalesce、Sum(default=0)只能作用于子查询筛选后剩下的行,那些被过滤掉的无数据队伍,子查询根本不会返回对应记录,外层主查询用Subquery关联时自然拿到None - 这也是不加外层filter时Sum的default能生效的原因:此时所有Equipo行都保留在结果集里,聚合逻辑能覆盖到无关联数据的场景
修复方案
直接把关联表的过滤条件放到Sum聚合自带的filter参数里,去掉子查询外层的跨表filter,这样所有Equipo记录都会保留在结果中,无匹配积分的场景会直接返回0,不会出现空值。这种写法比拆分9个独立子查询的表扫描次数更少,性能更高。
首先在视图文件头部补充缺失的导入:
from django.db.models import Sum, Value, IntegerField, F, Q from django.db.models.functions import Coalesce
替换原有视图中的查询逻辑:
def clasificacion_etapa2(request): equipos = Equipo.objects.annotate( puntos_lider1=Coalesce( Sum('lid1__puntos__puntos', filter=Q(lid1__puntos__etapa=2), default=0), Value(0), output_field=IntegerField() ), puntos_lider2=Coalesce( Sum('lid2__puntos__puntos', filter=Q(lid2__puntos__etapa=2), default=0), Value(0), output_field=IntegerField() ), puntos_lider3=Coalesce( Sum('lid3__puntos__puntos', filter=Q(lid3__puntos__etapa=2), default=0), Value(0), output_field=IntegerField() ), puntos_lider4=Coalesce( Sum('lid4__puntos__puntos', filter=Q(lid4__puntos__etapa=2), default=0), Value(0), output_field=IntegerField() ), puntos_gregario1=Coalesce( Sum('gre1__puntos__puntos', filter=Q(gre1__puntos__etapa=2), default=0), Value(0), output_field=IntegerField() ), puntos_gregario2=Coalesce( Sum('gre2__puntos__puntos', filter=Q(gre2__puntos__etapa=2), default=0), Value(0), output_field=IntegerField() ), puntos_gregario3=Coalesce( Sum('gre3__puntos__puntos', filter=Q(gre3__puntos__etapa=2), default=0), Value(0), output_field=IntegerField() ), puntos_jornalero1=Coalesce( Sum('jlg1__puntos__puntos', filter=Q(jlg1__puntos__etapa=2), default=0), Value(0), output_field=IntegerField() ), puntos_jornalero2=Coalesce( Sum('jlg2__puntos__puntos', filter=Q(jlg2__puntos__etapa=2), default=0), Value(0), output_field=IntegerField() ) ).annotate( puntos_equipo=F('puntos_lider1')+F('puntos_lider2')+F('puntos_lider3')+F('puntos_lider4')+F('puntos_gregario1')+F('puntos_gregario2')+F('puntos_gregario3')+F('puntos_jornalero1')+F('puntos_jornalero2') ).order_by('-puntos_equipo') return render(request,"PorraApp/clasificacion_etapa2.html",{"equipos":equipos})
备选方案(不推荐)
如果一定要保留原有拆分9个独立子查询的写法,需要给每个Subquery外层再套一层Coalesce,把Subquery返回的None转成0,但这种写法会产生更多次表关联查询,性能远差于上面的直接聚合写法,示例:
puntos_lider1=Coalesce( Subquery(puntos_lider1.values('puntos_lider1'),output_field=IntegerField()), Value(0) )
注:聚合的
filter参数在Django 2.0及以上版本原生支持,会在聚合计算时只统计符合条件的关联记录,不会过滤主表的Equipo行,完全适配你需要保留所有队伍、无积分时返回0的业务场景。
内容的提问来源于stack exchange,提问作者Antonio
相关产品推荐
相关产品推荐

