Python 带倒Q对象的WHERE子句中的子查询
我有两个模型,其中一个是指另一个:Python 带倒Q对象的WHERE子句中的子查询,python,django,postgresql,orm,Python,Django,Postgresql,Orm,我有两个模型,其中一个是指另一个: class A(models.Model): variable = models.BooleanField(default=False, null=False) b = models.ForeignKey(B, on_delete=models.CASCADE, related_name='
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
在对变量进行筛选时,我想反向跟踪该关系:
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))'
'SELECT "b"."id" FROM "b" INNER JOIN "a" ON ("b"."id" = "a"."b_id") WHERE "a"."variable" = True'
另一方面,当不反转Q表达式时
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))'
'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简短回答:对“所有B
对象与a
对象与变量=True
相关”的否定不是查询“*所有B
对象与a
对象与变量=False
”
您可以这样查询:
B.objects.filter(a__variable=False)
或者,如果字段为NULL
,则可以:
B.objects.filter(Q(a__variable=None) | Q(a__variable=False))
背景:否定存在量化表达
这是预期的行为。因为如果您以一对多的方式查询相关模型,Django ORM的设计者选择了存在量词∃ 超通用量词∀. 没有内在的最佳选择,尽管我认为人类将执行的大多数查询都是存在量化的*
存在量词的意思是“存在”,所以如果你写B.objects.filter(a__variable=True)
,你会要求B
对象,其中“存在一个相关的a
对象,且变量=True
”
但否定这一点的不是B
对象列表”,其中存在一个相关的a
对象,且variable=False
(现在让我们忽略NULL
花瓶)。实际上,一个B
对象有两个相关的a
对象,一个是variable=True
,另一个是variable=False
,它会出现在原始变量及其否定中
存在量化需求的否定,是该谓词否定的唯一量化变体。或者在数学方面:
,∃x:P(x)↔ ∀x:P(x)
因此,这意味着对查询“存在A
且variable=True
的所有B
对象”的否定是对查询“*所有相关A
对象都有一个非B
*的变量的所有B
对象”。请注意第二个查询中的all。因此,这意味着对于A
表中的每一行,我们需要对相关的“B”对象进行“迭代”,以检查所有这些变量
是否为真
。这并不是真正为JOIN
s“定制”的。在布尔字段
的情况下,我们可以使用GROUP BY
和MAX(..)
来检查是否至少存在一个这样的真
,从而约束它这不是真
。比如:
SELECT b.*, MAX(a.variable) AS mx
FROM b
LEFT OUTER JOIN b ON a.b_id = b.id
GROUP BY a.id
HAVING mx = FALSE OR mx IS NULL
但是这个“技巧”需要Django ORM查询生成器进行一些“高级”平铺。这可能在将来的版本中最终会得到支持,但无论如何,效率将大致相同。这是预期的行为。Django已经指定相关模型上的过滤器是使用存在量词完成的。存在量词的否定是带有否定谓词的通用量词。请参阅:对于您的情况,您可以使用B.objects.filter(a_u变量=False)
(或者严格地说B.objects.filter(Q(a_u变量=None)| Q(a_u变量=False))
,这与B.objects.filter(Q(a_u变量=True))不同
但是将a__变量=False
更改为a__变量=True
应该与在Q表达式前面放置一个倒位相同。如果我遵循正向的引用,即A.object.filter(Q(b_u-other_-var=True))
,为什么这会起作用呢?不,因为这会使它听起来不像“谓词逻辑”。假设有两个相关的A
s,一个是variable=True
,另一个是variable=False
,您希望查询B.objects.filter(~Q(A\u variable=True))
准确地包含查询B.objects.filter(Q(A\u variable=True))
(因为您反转了条件),但是这里,B
对象将出现在两个查询集中。