【发布时间】:2019-08-21 22:33:47
【问题描述】:
对于提前格式化碍眼的问题,我深表歉意。随意编辑我的问题以获得更好的可读性。
我有四个模型:
class Datasets(models.Model):
name = models.CharField(max_length=150)
description = models.TextField()
class Assay(models.Model):
dataset = models.ForeignKey(Datasets)
name = models.CharField(max_length=150)
type = models.CharField(max_length=150)
class Compounds(models.Model):
dataset = models.ForeignKey(Datasets)
name = models.TextField()
deleted = models.BooleanField(default=False)
class Assays(models.Model):
compound = models.ForeignKey(Compounds)
assay = models.ForeignKey(Assay)
value = models.DecimalField(max_digits=30, decimal_places=16)
deleted = models.BooleanField(default=False)
我正在使用用户输入构建查询,具体取决于所选的 Assay。我正在使用JOINs 根据反向关系过滤结果。用户选择Assay,然后我根据选择过滤化合物。用户还可以选择“No Assay”选项,该选项应返回未注册化验的化合物(即,Assays 模型中没有该化合物的条目)。
selected_assay_id = 5 # Received from frontend
no_assay_option_selected = True/False # Received from frontend
dataset_id = 1
filter_query = Q()
filter_query.add(Q(**{
'assays__assay__id': selected_assay_id,
'assays__compound__id': F('id'),
'assays__deleted': False
}), Q.OR)
if no_assay_option_selected:
filter_query.add(~Q(**{
'assays__deleted': False,
'assays__compound__id': F('id')
}), Q.OR)
compounds = Compounds.objects.filter(filter_query, dataset__id=dataset_id).distinct()
当我选择一种检测方法时,它的效果很好。当我选择“No Assay”时,效果很好。但是,当我选择一个化验和“无化验”时,所有化合物都会返回,而不是选择化验和“无化验”的化合物。当我检查原始 SQL 查询时,我意识到后面的查询有一个额外的部分:
-- Only 'No Assay' option selected
SELECT DISTINCT * FROM "compounds" WHERE (
NOT ("compounds"."id" IN
(SELECT U1."compound_id" FROM "compounds" U0
INNER JOIN "assays" U1 ON (U0."id" = U1."compound_id")
WHERE U1."compound_id" = (U0."id")) AND "compounds"."id" IN
(SELECT U1."compound_id" FROM "assays" U1
WHERE U1."deleted" = False)
)
AND "compounds"."dataset_id" = 1 AND "compounds"."deleted" = False
)
-- An assay is selected
SELECT DISTINCT * FROM "compounds"
INNER JOIN "assays" ON ("compounds"."id" = "assays"."compound_id")
WHERE (
"assays"."assay_id" = 5
AND "assays"."compound_id" = ("compounds"."id")
AND "assays"."deleted" = False
AND "compounds"."dataset_id" = 1
AND "compounds"."deleted" = False
)
-- An assay and 'No Assay' option selected
SELECT DISTINCT * FROM "compounds"
LEFT OUTER JOIN "assays" ON ("compounds"."id" = "assays"."compound_id")
WHERE (
(
(
"assays"."assay_id" = 5
AND "assays"."compound_id" = ("compounds"."id")
AND "assays"."deleted" = False
) OR NOT (
"compounds"."id" IN
(SELECT U1."compound_id" FROM "compounds" U0
INNER JOIN "assays" U1 ON (U0."id" = U1."compound_id")
WHERE (U1."compound_id" = (U0."id") AND U1."id" = ("assays"."id"))
) AND "compounds"."id" IN
(SELECT U1."compound_id" FROM "assays" U1
WHERE (U1."deleted" = False AND U1."id" = ("assays"."id"))
)
)
)
AND "compounds"."dataset_id" = 1 AND "compounds"."deleted" = False
)
这是最后一个查询中的额外部分:AND U1."id" = ("assays"."id") 会导致奇怪的结果。当我删除它并运行原始查询时,我得到了想要的结果。
我的问题是:为什么 Django 会这样做,我该如何解决?
【问题讨论】: