Django中用|合并大量QuerySet报错Expression tree is too large如何解决?
这问题我太熟了!你现在的写法是循环上千次用|合并QuerySet,每次合并都会给查询语句加一个新的OR条件,攒到1200次的时候,Django的表达式树深度直接突破上限,自然就报错了。而且这种写法不仅容易触发错误,查询效率也极低——相当于执行了1200次小查询再合并,完全没必要。
其实你要的结果用一次ORM查询就能搞定,根本不用循环合并,还能完美保持QuerySet类型,给你两个最优方案:
方案1:直接用关联查询(最简洁高效)
既然你要找的是某个project下所有functions对应的properties,完全可以通过外键关联直接跨表过滤,一步到位:
def properties(self, request, pk=None): project = self.get_object() if project is None: return Response({'detail': 'Missing project id'}, status=404) # 直接通过关联关系过滤,生成单条JOIN查询 properties = Property.objects.filter(function__project=project) return Response([PropertySerializer(x).data for x in properties])
这个写法的原理是利用Django ORM的正向关联查询:function__project表示Property关联的Function所属的Project,直接过滤等于目标project的记录。生成的SQL是一条带JOIN的查询,不管你的functions有多少条,都不会出现表达式树过大的问题,效率比循环合并高N倍。
方案2:用__in过滤(适合需要先处理functions的场景)
如果你的业务逻辑里需要先对functions做一些额外过滤(比如筛选特定状态的function),可以先拿到所有符合条件的function id,再用__in查询对应的properties:
def properties(self, request, pk=None): project = self.get_object() if project is None: return Response({'detail': 'Missing project id'}, status=404) functions = Function.objects.filter(project=project) # 拿到所有function的id,生成一个QuerySet(不会立即执行查询) function_ids = functions.values_list('id', flat=True) # 用__in过滤属于这些function的properties properties = Property.objects.filter(function_id__in=function_ids) return Response([PropertySerializer(x).data for x in properties])
这种写法生成的SQL会用IN条件,把所有function id作为参数传入,同样是单条查询,不会有表达式树的问题。而且values_list('id', flat=True)返回的是一个懒加载的QuerySet,不会提前把所有id加载到内存里,性能也很好。
为什么原来的写法会出问题?
你之前用的|操作符,本质上是把两个QuerySet的查询条件用OR拼接起来。循环1200次的话,最终的查询语句会变成WHERE (condition1) OR (condition2) OR ... OR (condition1200),这个嵌套的表达式树深度早就超过了Django默认的1000上限,自然就触发了Expression tree is too large错误。
而上面两种方案都是用单条查询完成需求,从根本上避免了这个问题,同时还能保持返回的是QuerySet类型,完美符合你的需求。
内容的提问来源于stack exchange,提问作者Snackoverflow

