如何将Django查询过滤器放在ON子句而不是WHERE子句中?

时间:2019-02-14 18:56:03

标签: django django-models django-rest-framework

在SQL中,条件的放置(无论是在ON子句中还是WHERE子句中)都会影响结果。 Django中的filter()似乎将条件放在WHERE子句中。

说我有这些模型:

class Nations(models.Model):
    deleted = models.BooleanField(default=False)


class People(models.Model):
    nation = models.ForeignKey(Nations, on_delete=models.CASCADE)
    deleted = models.BooleanField(default=False)

我想找到没有人的国家,我可以这样做:

SELECT nations.id, nations.deleted
FROM nations
LEFT JOIN people
   ON people.nation_id = nations.id
WHERE people.id IS NULL;

但是,可以说人们可以被软删除,我想找到其中没有不败之民的国家。我会的:

SELECT nations.id, nations.deleted
FROM nations
LEFT JOIN people
   ON people.nation_id = nations.id
   AND people.deleted IS FALSE
WHERE people.id IS NULL;

但是,在Django中,如果我这样做:

Nations.objects.filter(people__id__isnull=True, people__deleted=False)

这等同于查询:

SELECT nations.id, nations.deleted
FROM nations
LEFT JOIN people
   ON people.nation_id = nations.id
WHERE people.id IS NULL
   AND people.deleted IS FALSE;

与所需查询不同,该查询将不包括仅删除人员的国家!如何让Django将软删除检查移到ON子句中?

2 个答案:

答案 0 :(得分:3)

自Django 2.0以来,有一个鲜为人知的功能,名为FilteredRelation,该功能允许向关系中添加其他过滤器。结果是联接的ON子句中的附加条件:

nations = (Nations.objects
    .annotate(no_deleted=FilteredRelation(
        'people',
        condition=Q(people__deleted=False)))
    .filter(no_deleted=None)
)

print(str(nations.query))

生成的SQL看起来很像您所追求的:

SELECT "app_nations"."id",
       "app_nations"....
FROM "app_nations"
LEFT OUTER JOIN "app_people" no_deleted ON 
    ("app_nations"."id" = no_deleted."nations_id"
     AND (no_deleted."deleted" = FALSE))
WHERE no_deleted."id" IS NULL

答案 1 :(得分:0)

在阅读您的最后一句话时,您似乎想要包括没有人的国家和只有被删除的人的国家。要做到这一点,您只需exclude那些没有删除人员的国家即可:

qs = Nations.objects.exclude(people__deleted=False)