You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

关键优化点

  1. 替换原0分支的内存过滤逻辑:原代码用exclude(pk__in=[...])会先全量查询EvaluationGenerale再做内存过滤,改成note_moyenne__isnull=True直接通过SQL过滤,性能大幅提升
  2. 将prefetch_related和annotate提到循环外,避免重复执行相同QuerySet操作,减少冗余

原理说明

现在两个分支的QuerySet都包含note_moyenne字段,且基于同一个带GROUP BY的基础QuerySet,不管用|还是union(需加all=True)都能正常工作,SQL层面结构完全一致,不会再出现GROUP BY或列数不匹配的错误。

内容的提问来源于stack exchange,提问作者LadyNaur

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.11 07:25:23