提问人:Henrique 提问时间:10/25/2023 更新时间:11/16/2023 访问量:34
使用 Django ORM 查询具有两个 FK 的模型,这两个 FK 通过对第三个模型的 unique_together 约束相关联
Using Django ORM to query a Model with two FKs that are related through an unique_together constraint on a third Model
问:
我目前在创建一个 QuerySet 时面临着一个挑战,该 QuerySet 可以准确表示我的 Django 模型中的关系。我有一个配备两个外键的模型,FK1 和 FK2。这两个外键组合在一起时,会创建一个表示分类 (FK1, FK2 -> CLS.pk) 的元组。 这种关系通过另一个模型进行管理和表示,在该模型中,我存储了与这些外键和对应的记录以及生成的分类(FK1、FK2、CLS.pk)。
定义我的问题本身就是一个挑战,所以我试图简化这种关系,以说明我在这里试图实现的核心思想。
class Event(models.Model):
value = models.DecimalField(decimal_places=2, max_digits=11, default=0.0)
event_date = models.DateField()
local = models.ForeignKey(Local, on_delete=models.CASCADE, related_name='event_set')
origin = models.ForeignKey(Origin, on_delete=models.CASCADE, related_name='event_set')
class Meta:
db_table = 'db_event'
class Local(models.Model):
name = models.CharField(max_length=100, unique=True)
class Meta:
db_table = 'db_local'
class Origin(models.Model):
name = models.CharField(max_length=100, unique=True)
class Meta:
db_table = 'db_origin'
class Classification(models.Model):
name = models.CharField(max_length=100, unique=True)
class Meta:
db_table = 'db_classification'
class ClassificationPerOriginAndLocal(models.Model):
local = models.ForeignKey(Local, on_delete=models.CASCADE, related_name='classification_related_set')
origin = models.ForeignKey(Origin, on_delete=models.CASCADE, related_name='classification_related_set')
classification = models.ForeignKey(Classification, on_delete=models.CASCADE, related_name='classification_related_set')
class Meta:
db_table = 'db_classification_per_origin_and_local'
unique_together = ('local', 'origin',)
考虑一个“事件”模型,该模型旨在存储发生在特定来源和特定本地的事件的记录。稍后,我需要对每个事件进行分类,这种分类是通过元组“origin”和“local”实现的。
这个分类系统通过名为“ClassificationPerOriginAndLocal”的第三个模型进行管理,我在其中指定哪个“Classification”对象对应于一对给定的“origin”和“local”对象。
在 SQL 原始查询中,我会制定如下内容来获取“Classification.pk”:
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
如何使用(如果可能的话)Django ORM 来创建这个查询?
答:
from django.db.models import F
events_with_classification = Event.objects.annotate(
classification_id=F('local__classification_related_set__classification_id')
)
# Now, you can access the classification_id field for each Event object
for event in events_with_classification:
classification_id = event.classification_id
您应该能够访问查询集中每个“事件”的“classification_id”字段,并且它将包含“ClassificationPerOriginAndLocal”模型中指定的“Classification.pk”。
评论
经过长时间的挖掘其他 Stack Overflow 线程和 Django 文档,我设法使用 Subquery 和 OuterRef 解决了这个问题:
from django.db.models import OuterRef, Subquery
# Creating the subquery
subquery = (
ClassificationPerOriginAndLocal
.objects
.filter(local=OuterRef('local'), origin=OuterRef('origin'))
)
# Constructing the final query with annotation
final_query = (
Event
.objects
.annotate(classification=Subquery(subquery.values('classification')))
)
对于上下文,我找到的解决方案确保子查询仅返回每个关系的奇异值(本地 + origin -> 分类)。这是通过利用本地 + 源的唯一约束(使用 unique_together 设置)来强制执行的。
此外,如果你需要访问分类对象中的特定字段,你可以使用 Django 的 '__' 查找来实现这一点:
final_query = (
Event
.objects
.annotate(
classification=Subquery(subquery.values('classification')),
classification__name=Subquery(subquery.values('classification__name')
)
)
评论