You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何用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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.07 20:25:14