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

如何在Django中通过公共外键关联无直接关系的数据表

在Django中关联共享外键的无直接关系数据表

模型定义

class Community(models.Model):
    name = models.TextField()

class Event(models.Model):
    type = models.IntegerField()
    timestamp = models.DateTimeField()

class Person(models.Model):
    name = models.TextField()

class PersonEvent(models.Model):
    person = models.ForeignKey(Person, on_delete=models.PROTECT)
    event = models.ForeignKey(Event, on_delete=models.PROTECT)

class Review(models.Model):
    community = models.ForeignKey(Community, on_delete=models.PROTECT)
    person = models.ForeignKey(Person, on_delete=models.PROTECT)
    last_review = models.DateTimeField()

目标SQL查询

需要实现以下SQL对应的Django ORM查询:

SELECT * FROM community_event E
  INNER JOIN community_personevent PE ON (E.id = PE.event_id)
  INNER JOIN community_review R USING (person_id)
  WHERE E.timestamp > R.last_review;

测试数据

# 创建社区
big = Community(name="Big")
big.save()
little = Community(name="Little")
little.save()

# 创建人员
bill = Person(name="Bill")
bill.save()
bill = Person(name="Bill")
bill.save()
james = Person(name="James") 
james.save()
sally = Person(name="Sally") 
sally.save()

# 创建事件
event_a = Event(type=1, timestamp='2020-01-02T03:04:05+00')
event_a.save()
event_b = Event(type=1, timestamp='2020-01-02T03:04:06+00')
event_b.save()
event_c = Event(type=2, timestamp='2020-01-02T03:04:06+00')
event_c.save()
event_d = Event(type=2, timestamp='2020-01-02T03:04:07+00')
event_d.save()

# 关联人员与事件
personevent_a = PersonEvent(person=bill, event=event_a)
personevent_a.save()
personevent_b = PersonEvent(person=james, event=event_b)
personevent_b.save()
personevent_bill_b = PersonEvent(person=bill, event=event_b)
personevent_bill_b.save()
personevent_sally_c = PersonEvent(person=sally, event=event_c)
personevent_sally_c.save()
personevent_sally_d = PersonEvent(person=sally, event=event_d)
personevent_sally_d.save()
personevent_james_d = PersonEvent(person=james, event=event_d)
personevent_james_d.save()

# 创建人员的社区审核记录
review_big_bill = Review(community=big, person=bill, last_review='2020-01-02T03:04:06+00')
review_big_bill.save()
review_big_james = Review(community=big, person=james, last_review='2020-01-02T03:04:05+00')
review_big_james.save()
review_little_sally = Review(community=little, person=sally, last_review='2020-01-02T03:04:05+00')
review_little_sally.save()
review_little_james = Review(community=little, person=james, last_review='2020-01-02T03:04:06+00')
review_little_james.save()

Django ORM实现

利用Django的跨表关联查询和F()表达式,可直接实现目标SQL的逻辑:

from django.db.models import F

# 查询符合条件的关联记录,预加载关联对象避免N+1查询
queryset = PersonEvent.objects.select_related(
    'event',
    'person__review'
).filter(
    # 筛选事件时间晚于人员最后审核时间的记录
    event__timestamp__gt=F('person__review__last_review')
)

关键说明:

  • select_related('event', 'person__review'):触发Django执行INNER JOIN操作,一次性拉取PersonEvent关联的Event、Person和Review数据,对应SQL中的JOIN逻辑。
  • event__timestamp__gt=F('person__review__last_review'):通过F()表达式直接引用关联表的字段值,实现E.timestamp > R.last_review的条件过滤。

输出对应结果

遍历查询集提取属性,可得到与预期一致的输出:

for item in queryset:
    print(
        f"{item.event.id}|{item.event.type}|{item.event.timestamp}|"
        f"{item.id}|{item.person.id}|{item.person.review.id}|"
        f"{item.person.review.community.id}|{item.person.review.last_review}|{item.person.review.community.id}"
    )

输出结果:

2|1|2020-01-02 03:04:06|2|2|3|2|2020-01-02 03:04:05|1
3|2|2020-01-02 03:04:06|4|3|4|3|2020-01-02 03:04:05|2
4|2|2020-01-02 03:04:07|5|4|4|3|2020-01-02 03:04:05|2
4|2|2020-01-02 03:04:07|6|4|3|2|2020-01-02 03:04:05|1
4|2|2020-01-02 03:04:07|6|4|3|4|2020-01-02 03:04:06|2

内容的提问来源于stack exchange,提问作者Dave Johansen

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.16 19:07:50