我有两种模型,其中一种是指另一种:
class A(models.Model):
variable = models.BooleanField(default=False, null=False)
b = models.ForeignKey(B, on_delete=models.CASCADE, related_name='as', related_query_name="a")
class B(models.Model):
pass
我想在过滤variable
时追随该关系:
B.objects.filter(~Q(a__variable))
问题:
这样会在where
子句中产生一个额外的子查询:
'SELECT "b"."id" FROM "b" WHERE NOT ("b"."id" IN (SELECT U1."b_id" FROM "a" U1 WHERE U1."variable" = True))'
另一方面,当不反转Q表达式时
B.objects.filter(Q(a__variable))
联接是“正确”完成的,即在where
子句之外:
'SELECT "b"."id" FROM "b" INNER JOIN "a" ON ("b"."id" = "a"."b_id") WHERE "a"."variable" = True'
注意::我仅以布尔值为例(可以将其转换为False
)
我正在使用Django 2.0.4和Postgres 9.6.2
答案 0 :(得分:3)
简短答案:“所有带有相关B
对象和A
对象的variable = True
对象的求反是不是查询“ *所有与B
相关的A
对象相关的variable = False
对象”。
您可以像这样查询:
B.objects.filter(a__variable=False)
或者该字段是NULL
允许的:
B.objects.filter(Q(a__variable=None) | Q(a__variable=False))
这是预期行为。由于如果您以一对多的方式查询 related 模型,则Django ORM的设计人员已选择 existential量词∃而不是通用量词∀ 。虽然我认为人类将执行的大多数查询都是存在量化的*,但没有固有的最佳选择。
存在量词的意思是“存在”,因此如果您写B.objects.filter(a__variable=True)
,则会要求B
对象,其中“ 存在一个相关的A
对象variable=True
”。
但是,否定不是 B
个对象的列表,“ 存在的地方有一个相关的{{1} } A
”对象(让我们暂时忽略variable=False
花瓶)。实际上,在两个原始变体中,一个NULL
对象具有两个相关的B
对象,一个带有A
,一个带有variable = True
的对象会出现,及其否定。
存在量化要求的否定是该谓词否定的普遍量化变体。或在数学中:
¬∃ x :P(x)↔ x :¬P(x)
因此,这意味着查询“ 所有variable = False
与B
的A
对象的否定查询” *与 all 相关的所有variable=True
对象的所有B
对象都具有一个 not A
*“的variable
。注意第二个查询中的 all 。因此,这意味着对于B
表中的每一行,我们需要“迭代”相关的“ B”对象,以检查所有这些A
是否都是 not variable
。对于True
来说,这并不是真正的“定制”。对于JOIN
,我们可以用BooleanField
和GROUP BY
来做到这一点,以检查是否至少有一个这样的MAX(..)
存在,并且因此将其约束为不是 TRUE
。像这样:
TRUE
但是Django ORM查询构建器需要使用此“ 技巧”进行一些“高级” 平铺。也许在将来的版本中最终会获得支持,但是无论如何,效率将大致相同。