如何用Django ORM实现双外键关联的分类查询?
实现方法
首先假设你的模型定义大致如下(补充必要关联关系):
from django.db import models class Local(models.Model): # 自定义业务字段 pass class Origin(models.Model): # 自定义业务字段 pass class Classification(models.Model): # 自定义业务字段 pass class ClassificationPerOriginAndLocal(models.Model): local = models.ForeignKey(Local, on_delete=models.CASCADE) origin = models.ForeignKey(Origin, on_delete=models.CASCADE) classification = models.ForeignKey(Classification, on_delete=models.CASCADE) class Meta: unique_together = ('local', 'origin') class Event(models.Model): local = models.ForeignKey(Local, on_delete=models.CASCADE) origin = models.ForeignKey(Origin, on_delete=models.CASCADE) # 其他业务字段 pass
方法1:用Subquery关联获取分类ID(推荐,保留Event对象结构)
利用Subquery和OuterRef匹配Event的local_id与origin_id,结合unique_together约束确保唯一匹配,效果和原生INNER JOIN一致:
from django.db.models import Subquery, OuterRef events = Event.objects.filter( # 过滤存在对应关联记录的Event local__classificationperoriginandlocal__origin=models.F('origin') ).annotate( classification_id=Subquery( ClassificationPerOriginAndLocal.objects.filter( local_id=OuterRef('local_id'), origin_id=OuterRef('origin_id') ).values('classification_id')[:1] ) ) # 访问结果示例 for event in events: print(event.id, event.classification_id)
如果需要保留所有Event(包括无对应分类的,对应LEFT JOIN逻辑),去掉filter部分即可,此时无匹配的classification_id会返回None。
方法2:用values直接返回字段字典
若仅需特定字段、不需要完整Event对象,可通过values结合关联查询实现:
from django.db.models import F events_with_clsf = Event.objects.filter( local__classificationperoriginandlocal__origin=F('origin') ).values( # 列出需要的Event字段 'id', 'local_id', 'origin_id', # 关联获取分类ID classification_id=F('local__classificationperoriginandlocal__classification_id') ).distinct() # 返回结果为字典列表 for item in events_with_clsf: print(item['id'], item['classification_id'])
方法3:直接执行原生SQL
如果ORM方式无法满足复杂需求,也可以直接执行你提供的原生SQL:
events = Event.objects.raw(''' SELECT ev.*, clsf.classification_id FROM db_event as ev INNER JOIN db_classification_per_origin_and_local as clsf ON clsf.local_id = ev.local_id AND clsf.origin_id = ev.origin_id ''') # 访问结果示例 for event in events: print(event.id, event.classification_id)
内容的提问来源于stack exchange,提问作者Henrique
相关产品推荐
相关产品推荐

