如何通过无直接关联的复杂查询获取Django模型数据
问题:通过Django模型复现复杂关联查询获取EIH数据
现有简化后的Django模型结构如下:
class PE(models.Model): # table p_e p_e_id = models.AutoField(primary_key=True) p_o = models.ForeignKey('PO', models.DO_NOTHING) p_r = models.ForeignKey('PR', models.DO_NOTHING) s_o = models.ForeignKey('SO', models.DO_NOTHING) item = models.ForeignKey(Items, models.DO_NOTHING, related_name='pe_item') p_e_is_possible = models.BooleanField() class EIH(models.Model): # table e_i_h e_i_h_id = models.AutoField(primary_key=True) target_item = models.ForeignKey('Items', models.DO_NOTHING,related_name='target_item') component_item = models.ForeignKey('Items', models.DO_NOTHING,related_name='component_item') p_o = models.ForeignKey('PO', models.DO_NOTHING) p_r = models.ForeignKey('PR', models.DO_NOTHING) class Items(models.Model): item_id = models.AutoField(primary_key=True)
当前使用的原生SQL查询逻辑是:
SELECT * FROM p_e INNER JOIN e_i_h ON e_i_h.target_item_id = p_e.item_id AND e_i_h.component_item_id = -1 AND (e_i_h.p_o_id = p_e.p_o_id OR (p_e.p_e_is_possible = True AND p_e.s_o_id != -1) )
需求是仅通过Django ORM获取符合上述SQL条件的EIH数据集,但尝试的关联查询无法正确引用PE模型的字段(如p_e_is_possible),比如以下写法存在问题:
pe_queryset = PE.objects.filter(item_id=15) for pe in pe_queryset: erp = EIH.objects.get(Q(p_o_id= pe.p_o.p_o_id) | Q(pe.p_e_is_possible=True), item_id=pe.item.item_id, component_item_id=-1)
或:
erp = EIH.objects.get(Q(p_o_id= pe.p_o.p_o_id) | Q(target_item__p_e__p_e_is_possible=True), item_id=pe.item.item_id, component_item_id=-1)
问题在于无法正确将PE的字段条件关联到EIH的查询中,如何复现原SQL的查询逻辑?
解决方案
可以通过Django的Q对象、F表达式和反向关联(利用Items的related_name)直接复现原生SQL的内连接逻辑,无需循环查询:
from django.db.models import Q, F # 构建符合条件的EIH查询集 eih_queryset = EIH.objects.filter( # 对应SQL中的 e_i_h.component_item_id = -1 component_item_id=-1, # 通过反向关联关联PE表,对应 e_i_h.target_item_id = p_e.item_id target_item__pe_item__isnull=False, ).filter( # 复现SQL中的OR条件分支 Q(p_o_id=F('target_item__pe_item__p_o_id')) | Q(target_item__pe_item__p_e_is_possible=True, target_item__pe_item__s_o_id__ne=-1) ).distinct()
关键说明:
- 反向关联引用:
target_item__pe_item是通过Items模型中PE的related_name='pe_item'实现的反向关联,对应SQL中e_i_h.target_item_id = p_e.item_id的关联逻辑。 - F表达式:
F('target_item__pe_item__p_o_id')用于在查询中直接引用关联表的字段值,实现e_i_h.p_o_id = p_e.p_o_id的等值匹配。 - Q对象组合:用
|连接两个Q对象,精准复现原生SQL中的OR条件分支。 - distinct():如果存在一对多关联导致重复数据,用
distinct()去重,确保结果和原生SQL一致。
如果需要筛选特定item_id(比如示例中的15),只需在过滤条件中添加:
target_item__item_id=15
最终完整查询:
eih_queryset = EIH.objects.filter( component_item_id=-1, target_item__item_id=15, target_item__pe_item__isnull=False, ).filter( Q(p_o_id=F('target_item__pe_item__p_o_id')) | Q(target_item__pe_item__p_e_is_possible=True, target_item__pe_item__s_o_id__ne=-1) ).distinct()
内容的提问来源于stack exchange,提问作者Will Heeme
相关产品推荐
相关产品推荐

