Django3.0空列表__in取反的When生成错误SQL报ProgrammingError
问题根因
这是Django 3.0及更早版本的ORM优化缺陷:当ORM处理__in=[]条件时,会默认将正向Q查询优化为永假表达式,但对应取反Q对象的优化逻辑存在缺失,没有把~Q(__in=[])正确转换为永真表达式,反而直接清空了条件内容,最终生成CASE WHEN THEN的非法SQL语法,触发django.db.utils.ProgrammingError报错。
两个对照示例不会触发问题的原因:
- 正向
Q(__in=[])的优化逻辑完整,ORM会直接判定该条件永假,Case语句直接走默认值,因此生成的SQL逻辑正常 __in参数非空时不会触发空列表的特殊优化逻辑,取反操作可以正常生成SQL条件
Django 3.2及以上版本已经修复了该缺陷,后续升级版本后可以直接使用原有写法。
不升级Django的解决方案
最稳妥的方案是提前对__in的查询参数做判空处理,动态构造When条件,绕过ORM的优化bug:
# 这里的unit_list是你要传入__in查询的参数 unit_list = [] # 空列表时提前处理条件:~Q(field__in=[]) 等价于永真条件 if not unit_list: when_condition = When(True, then=Value(False)) else: when_condition = When(~Q(case__assigned_unit__in=unit_list), then=Value(False)) # 执行查询 Activity.objects.all().annotate( employee_authorized=Case( when_condition, default=Value(True), output_field=BooleanField(), ) )
如果不想做分支判断,也可以直接将空列表场景下的取反条件替换为通用永真表达式,比如Q(pk__isnull=False),也可以达到同样的效果,避免触发bug。
内容的提问来源于stack exchange,提问作者Rik Schoonbeek
相关产品推荐
相关产品推荐

