Django ORM合并查询集时字段冲突返回重复结果问题
Django QuerySet合并返回重复记录问题修复
原有问题代码
cameraSystems = DWCloudSystem.objects.filter(isShared=False).filter(company_key=comp) cameraSystems = cameraSystems | DWCloudSystem.objects.filter(isShared=True).filter(location__owners=comp)
关联模型说明
DWCloudSystem模型字段:company_key:外键字段,关联Company模型对象location:外键字段,关联place(地点)模型对象isShared:布尔类型字段
place模型字段:owners:多对多字段,关联Company模型对象
预期查询规则
isShared=False的私有记录:仅company_key匹配当前公司comp时可访问isShared=True的共享记录:满足以下任一条件即可访问- 当前公司
comp在记录对应地点的owners列表中 - 当前公司
comp是记录本身的company_key归属公司(即使不在地点owners列表中也有权限)
- 当前公司
故障现象
- 两个查询子集通过
|合并后返回重复记录 - 即使所有记录
isShared=True、理论上第一个查询子集结果为空时,重复记录依然存在 - 两个查询子集单独执行时返回结果正常
故障根因
- 用
|运算符合并QuerySet时,Django底层生成的是UNION ALL语句,不会自动对结果做去重处理。 - 查询关联多对多字段
location__owners时,SQL会执行表连接操作,只要单条主表记录在关联的多对多表中匹配到多行,主表记录就会被重复输出,哪怕两个查询子集的筛选条件完全互斥,表连接产生的重复行也不会被自动消除。 - 原有代码逻辑遗漏:共享记录的查询条件没包含「归属公司可直接访问」的规则,不符合预期。
修复方案
方案1:Q对象组合条件 + distinct()去重(优先选用)
用Q对象在单个QuerySet中组合所有筛选规则,最后调用distinct()消除表连接产生的重复行,后续支持链式调用ORM的过滤、排序等方法,灵活性最高:
from django.db.models import Q cameraSystems = DWCloudSystem.objects.filter( # 私有记录规则 Q(isShared=False, company_key=comp) | # 共享记录规则 Q(isShared=True) & (Q(location__owners=comp) | Q(company_key=comp)) ).distinct()
方案2:用union()方法合并查询集
如果要保留两个查询子集分开编写的形式,可以用QuerySet自带的union()方法,它底层生成UNION语句会自动去重,注意补全共享记录的缺失筛选条件:
# 私有记录查询集 qs_private = DWCloudSystem.objects.filter(isShared=False, company_key=comp) # 共享记录查询集:补全归属公司访问权限 qs_shared = DWCloudSystem.objects.filter( isShared=True ).filter( Q(location__owners=comp) | Q(company_key=comp) ) # union默认自动去重,无需额外调用distinct cameraSystems = qs_private.union(qs_shared)
注意:
union()合并生成的QuerySet对后续链式操作有部分限制(比如过滤、排序时不能随意使用关联字段),如果后续还要对结果做二次筛选,优先选择方案1。
内容的提问来源于stack exchange,提问作者RandomDev2345
相关产品推荐
相关产品推荐

