好的,以下是我已经解决的方案,尽管它远非令人满意。
我为我所有的模型添加了一个抽象基类:
class MyModel(models.Model):
class Meta:
abstract = True
def pre_delete_handler(self):
pass
信号处理程序捕获此模型子类的任何pre_delete
事件:
def pre_delete_handler(sender, instance, **kwargs):
if isinstance(instance, MyModel):
instance.pre_delete_handler()
models.signals.pre_delete.connect(pre_delete_handler)
在我的每个模型中,我通过在
pre_delete_handler
方法中抛出异常来模拟任何"
ON DELETE RESTRICT
"关系,如果存在子记录。
class RelatedRecordsExist(Exception): pass
class SomeModel(MyModel):
...
def pre_delete_handler(self):
if children.count():
raise RelatedRecordsExist("SomeModel has child records!")
在任何数据被修改之前,这将终止删除操作。
不幸的是,在 pre_delete 信号中无法更新任何数据(例如模拟 ON DELETE SET NULL),因为 Django 在发送信号之前已经生成了要删除对象的列表。Django 这样做是为了避免陷入循环引用并防止不必要地多次发送对象信号。
现在,确保可以执行删除的责任由调用代码承担。为了协助此操作,每个模型都有一个 prepare_delete() 方法,该方法通过 self.related_set.clear() 或类似方法来设置键为 NULL:
class MyModel(models.Model):
...
def prepare_delete(self):
pass
为了避免在我的
views.py
和
models.py
中改变太多的代码,我重写了
MyModel
上的
delete()
方法来调用
prepare_delete()
:
class MyModel(models.Model):
...
def delete(self):
self.prepare_delete()
super(MyModel, self).delete()
这意味着通过
obj.delete()
显式调用的任何删除操作将按预期工作,但如果从相关对象级联删除或通过
queryset.delete()
完成删除操作,并且调用代码没有确保必要的所有链接都被断开,则
pre_delete_handler
将抛出异常。
最后,我已经添加了类似的
post_delete_handler
方法到模型中,在
post_delete
信号上调用,让模型清除任何其他数据(例如删除
ImageField
的文件)。
class MyModel(models.Model):
...
def post_delete_handler(self):
pass
def post_delete_handler(sender, instance, **kwargs):
if isinstance(instance, MyModel):
instance.post_delete_handler()
models.signals.post_delete.connect(post_delete_handler)
我希望这能对某些人有所帮助,而且这段代码可以轻松地重新编排成更易用的形式。
如果有任何改进建议,欢迎提出。