Django自定义过滤器报错:勾选0与其他选项需GROUP BY
Django自定义MultipleChoiceFilter合并查询报错问题
我写了一个关联MultipleChoiceFilter的自定义过滤器,选项包括0、1、1.75、2.5、3.25和4:
def filtre_personnalise(self,queryset, name,value): query=FicheIdentification.objects.none() for i in value: if i=='0': query|=queryset.prefetch_related(Prefetch('entreprise',Entreprise.objects.all())).exclude(pk__in=[x.entreprise.siret for x in EvaluationGenerale.objects.all()]) else : query|=queryset.prefetch_related(Prefetch('entreprise',Entreprise.objects.all())).annotate(note_moyenne=Avg('entreprise__evaluationgenerale__note')).filter(note_moyenne__range=(float(i),float(i)+0.75)) return query
单独勾选每个选项返回结果正常,同时勾选多个非0选项也没问题,但只要勾选0和其他选项就报错:
a GROUP BY clause is required before HAVING
尝试用.union替换|后又出现新错误:
django.db.utils.ProgrammingError: each UNION query must have the same number of columns
我猜测问题和annotate的使用有关,求解决思路。
问题根源
没错,问题核心就是两个分支的QuerySet结构不一致:
- 非0分支的QuerySet通过
annotate额外添加了note_moyenne字段,且因使用Avg聚合函数自动触发GROUP BY逻辑 - 0分支的QuerySet没有
annotate,字段少一列,也无GROUP BY逻辑
用|合并时,Django尝试拼接不同结构的QuerySet,导致SQL层面GROUP BY/HAVING冲突;用union时则直接因列数不匹配报错。
解决思路:统一两个分支的QuerySet结构
给0分支的QuerySet也添加note_moyenne字段,保证聚合逻辑一致,同时优化原有低效查询:
from django.db.models import Avg def filtre_personnalise(self, queryset, name, value): # 先构建统一的基础QuerySet,提前完成预取和聚合 base_queryset = queryset.prefetch_related( Prefetch('entreprise', Entreprise.objects.all()) ).annotate( note_moyenne=Avg('entreprise__evaluationgenerale__note') ) query = FicheIdentification.objects.none() for i in value: if i == '0': # 0选项对应无EvaluationGenerale的记录,即note_moyenne为null的情况 query |= base_queryset.filter(note_moyenne__isnull=True) else: min_note = float(i) max_note = min_note + 0.75 query |= base_queryset.filter(note_moyenne__range=(min_note, max_note)) return query
关键优化点
- 替换原0分支的内存过滤逻辑:原代码用
exclude(pk__in=[...])会先全量查询EvaluationGenerale再做内存过滤,改成note_moyenne__isnull=True直接通过SQL过滤,性能大幅提升 - 将
prefetch_related和annotate提到循环外,避免重复执行相同QuerySet操作,减少冗余
原理说明
现在两个分支的QuerySet都包含note_moyenne字段,且基于同一个带GROUP BY的基础QuerySet,不管用|还是union(需加all=True)都能正常工作,SQL层面结构完全一致,不会再出现GROUP BY或列数不匹配的错误。
内容的提问来源于stack exchange,提问作者LadyNaur
相关产品推荐
相关产品推荐

