删除未使用的父模型

时间:2018-06-10 12:48:36

标签: django

我有这两个模型:

Parent(models.Model):
   name=models.CharField(max_length=10)

Child(models.Model):
   parent=models.ForeignKey(Parent)
   name=models.CharField(max_length=10)

我正在寻找一种有效的方法来删除没有孩子的父母。目前,我正在使用RAW SQL:

 from django.db import connection
 SQL="DELETE FROM app_parent WHERE app_parent.id NOT IN (SELECT id FROM app_child"

哪个运行两个查询,我仍然想使用Django ORM。对于ORM,我首先获得孩子列表,然后检查父ID是否存在。

有更快的方法吗?

1 个答案:

答案 0 :(得分:1)

使用LEFT OUTER JOIN

您可以Parent使用.filter(..)检测没有孩子的related_name,并检查该关系__isnull

所以我们可以通过写一下 no 孩子的Parent列表:

Parent.objects.filter(child__isnull=True).distinct()

.distinct()在这里很重要,否则父母可以多次次。在窗帘后面,Django将执行一个看起来像这样的查询:

SELECT DISTINCT `parent`.*
FROM `parent`
LEFT OUTER JOIN `child` ON (`parent`.`id` = `child`.`parent_id`)
WHERE (`child`.`id` IS NULL)

我们可以.delete() Parent s:

Parent.objects.filter(child__isnull=True).distinct().delete()

使用EXISTS查询

我们也可以使用存在的查询:

from django.db.models import Exists, OuterRef

Parent.objects.annotate(
    has_children=Exists(
        Child.objects.filter(parent=OuterRef('pk').values('id'))
    )
).where(has_children=False).delete()

因此,我们在每个Parent注释Child是否存在parentpk的{​​{1}}这一事实。然后我们Parent注意这个注释应该是.filter(..)

这将产生如下查询:

False

受保护的父对象(通过其他关系)

但请注意,如果仍然通过其他SELECT `parent`.* FROM `parent` WHERE NOT EXISTS ( SELECT `child`.`id` FROM `child` WHERE `child`.`parent_id` = `parent`.`id` ) = True 引用Parent,则可能会失败。例如,如果不仅有一个孩子,而是一个引用ForeignKey的{​​{1}}关系,那么Uncle可能会失败,因为仍有Parent个指没有孩子的一个或多个.delete()