Django模型ON DELETE CASCADE策略,模拟ON DELETE RESTRICT的通用解决方案

3 投票
3 回答
3772 浏览
提问于 2025-04-15 17:05

我想删除一个模型的实例,但前提是它没有其他类的实例通过外键指向它。根据Django的文档:

当Django删除一个对象时,它会模拟SQL中的“级联删除”行为——也就是说,任何指向要删除对象的外键的对象都会和它一起被删除。

举个例子:

class TestA(models.Model)
    name = models.CharField()

class TestB(models.Model)
    name = models.CharField()
    TestAs = models.ManyToManyField(TestA)

# More classes with a ManyToMany relationship with TestA
# ........

我想要类似这样的功能:

tA = TestA(name="testA1")
tB = TestB(name="testB1")
tB.testAs.add(tA)

t = TestA.objects.get(name="testA1")

if is_not_foreignkey(t):
    t.delete()
else:
    print "Error, some instance is using this"

应该打印出错误。我知道我可以检查特定实例的外键集合,比如在这个例子中检查t.TestB_set(),但我想要一个更通用的解决方案,适用于任何给定的模型。

3 个回答

0

在Django 1.3版本中,CollectedObjects()这个功能被移除了——下面是现在的做法:

from compiler.ast import flatten
from django.db import DEFAULT_DB_ALIAS
from django.contrib.admin.util import NestedObjects

def delete_obj_if_no_references(obj):
    collector = NestedObjects(using=DEFAULT_DB_ALIAS)
    collector.collect([obj])
    objs = flatten(collector.nested())
    if len(objs) == 1 and objs[0] is obj:
        obj.delete()
        return True
    return False
0

检查相关对象的长度

t=TestA.objects.get(name="textA1")
if not t.testB_set.all().count():#related members
  t.delete()
2

我终于解决了这个问题,使用了这个链接中的方法:可为空的外键和删除被引用的模型实例。这个解决方案看起来是这样的:

    # Check foreign key references
    instances_to_be_deleted = CollectedObjects()
    object._collect_sub_objects(instances_to_be_deleted)

    # Count objects to delete
    count_instances_to_delete = 0
    for k in instances_to_be_deleted.unordered_keys():
        count_instances_to_delete += len(instances_to_be_deleted[k])

    if count_instances_to_delete == 1:
        object.delete()
    else:
        pass

撰写回答