【问题标题】:Delete unused parent models删除未使用的父模型
【发布时间】:2018-06-10 12:48:35
【问题描述】:

我有这两个模型:

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 是否存在。

有没有更快的方法?

【问题讨论】:

  • 这实际上仍然是一个查询,但它有子查询。然而,它将以相同的时间复杂度运行。我们可以加快速度。
  • 加快速度如何@WillemVanOnsem

标签: django


【解决方案1】:

带有LEFT OUTER JOIN

您可以通过.filter(..)ing 与related_name 来检测没有孩子的Parents,并检查该关系是否__isnull

所以我们可以通过以下方式获得Parents 的列表 no 孩子:

Parent.objects<b>.filter(child__isnull=True).distinct()</b>

.distinct() 在这里很重要,否则父级可能会出现多次次。在幕后,Django 将执行如下查询:

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

然后我们可以.delete()那些Parents:

Parent.objects.filter(child__isnull=True)<b>.distinct().delete()</b>

使用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 是否存在具有parentpkParentChild。然后我们.filter(..) 说明这个注解应该是False

这将导致如下查询:

SELECT `parent`.*
FROM `parent`
WHERE NOT EXISTS (
    SELECT `child`.`id`
    FROM `child`
    WHERE `child`.`parent_id` = `parent`.`id`
) = True

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

但是请注意,如果 Parent 仍然通过其他 ForeignKeys 被引用,这可能会失败。例如,如果不仅有一个孩子,还有一个 Uncle 关系,它引用了 Parent,那么 .delete() 可能会失败,因为仍然有 Uncles 引用一个或多个 Parent没有孩子。

【讨论】:

  • 谢谢@willem Van Onsem。稍微检查一下可能的其他相关模型,它是完美的并且在速度上略有提升。
  • @NieSelam:第二次尝试怎么样(Exists)。能否提供时间。也许这对未来的读者会很好。
  • 好的,让我试试并粘贴我的结果。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2020-01-07
  • 2015-08-12
  • 2011-07-31
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多