如何通过多对一关系中同一相关对象的两个属性在 django 中进行过滤?

Posted

技术标签:

【中文标题】如何通过多对一关系中同一相关对象的两个属性在 django 中进行过滤?【英文标题】:How to filter in django by two properties of the same related object in a many-to-one relationship? 【发布时间】:2017-10-12 18:55:03 【问题描述】:

我的模型结构如下所示:

class OneModel(model):
    not_important = IntegerField()


class RelatedModel():
    propertyA = IntegerField()
    propertyB = IntegerField()
    original = ForeignKey(OneModel, related_name='related')   

我正在寻找一个原生 django 解决方案(没有原始 sql),基本上重新创建这个查询:

select * from OneModel om where not exists
(select id from RelatedModel rm where original_id = om.id and propertyA = 1 and propertyB = 2);

这是我尝试过的:

OneModel.objects.exclude(related__propertyA=1, related__propertyB=2)

不幸的是,这会选择OneModel 对象,这些对象既不propertyA=1相关,也不propertyB=2相关,而不是不相关的对象'没有与两个条件匹配的单个相关

这是我的 django 查询生成的 sql:

SELECT lots_of_fields
FROM "OneModel"
WHERE NOT ("OneModel"."id" IN (SELECT U1."original_id" AS Col1
                               FROM "RelatedModel" U1
                               WHERE U1."PropertyA" = 1)
           AND
           "OneModel"."id" IN (SELECT U1."original_id" AS Col1
                               FROM "RelatedModel" U1
                               WHERE U1."PropertyB" = 2))

为了清楚起见,我的问题不在于使用 "id" in 而不是存在,而是在于查询的逻辑。

我尝试过使用 Q 对象,但想不出任何方法来解决这个问题。我还查看了 F 对象,但它们似乎也不相关。有什么方法可以在纯 django 中表达这一点,还是我有决心编写 SQL?

至于为什么不只使用 SQL,我承认这更多是因为自豪/想要学习新事物而不是其他任何东西。

【问题讨论】:

【参考方案1】:

这实际上在文档中有所介绍——只是有点难找。

Here 是链接:向下滚动一点到绿色 “注释”部分。

它本质上是说基于外键的排除有点不直观,你不能在一个查询中做到这一点。相反,这应该可行(我只是将他们的代码移植到你的名字:

query = OneModel.objects.exclude(
    related__in=RelatedModel.objects.filter(
        propertyA=1,
        propertyB=2,
    ),
)

Django 对此进行了优化,虽然看起来好像发生了两个查询,但实际上只调用了一次数据库。感兴趣的可以通过query.query查看这个SQL调用。

【讨论】:

这看起来正是我想要的。明天我会检查并标记为已接受。谢谢。

以上是关于如何通过多对一关系中同一相关对象的两个属性在 django 中进行过滤?的主要内容,如果未能解决你的问题,请参考以下文章

django - 从多对一关系中获取对象集

Django 在多对一关系中引用特定对象

JPA - 如何在多对一关系中检查条件

如何通过多对多关系获取与同一张表相关的行 - Laravel

HQL 检索多对一关系中不同实体的列表

如何使通过多对多关系连接的模型在 Django admin 中同时在两个地方可编辑?