我有一个ForeignKey,在我的模型中可以为null,以模拟模型之间的松散耦合。看起来有点像:
class Message(models.Model):
sender = models.ForeignKey(User, null=True, blank=True)
sender_name = models.CharField(max_length=255)
在保存时,发件人名称将写入sender_name属性。现在,我希望能够删除发件人引用的User实例并保留消息。
开箱即用,只要删除User实例,此代码就会始终导致已删除的邮件。所以我认为信号处理程序是个好主意。
def my_signal_handler(sender, instance, **kwargs):
instance.message_set.clear()
pre_delete.connect(my_signal_handler, sender=User)
可悲的是,这绝不是一个解决方案。不知怎的,Django首先收集它想要删除的内容,然后触发pre_delete处理程序。
有什么想法吗?我脑子里的结是哪里的?
答案 0 :(得分:13)
Django确实模仿了SQL的ON DELETE CASCADE
行为,并且没有开箱即用的文档来改变它。他们提到这一点的文档接近本节末尾:Deleting objects。
你是对的,Django收集所有相关的模型实例,然后为每个实例调用预删除处理程序。信号的发送者将是要删除的模型类,在这种情况下是Message
,而不是User
,这使得很难检测到用户触发的级联删除与正常情况之间的差异删除...特别是因为删除User类的信号是最后一个,因为这是最后一次删除: - )
但是,您可以在调用User.delete()函数之前获取Django建议删除的对象列表。每个模型实例都有一个名为_collect_sub_objects()
的半私有方法,它使用指向它的外键编译实例列表(它编译此列表而不删除实例)。您可以通过查看delete()
中的django.db.base
来了解如何调用此方法。
如果这是您自己的对象之一,我建议在您的实例上覆盖delete()
方法以运行_collect_sub_objects(),然后在调用超类删除之前中断ForeignKeys。由于您正在使用内置的Django对象,您可能会发现它太难以进行子类化(尽管可以将您自己的User对象替换为django),您可能必须依赖视图逻辑来运行_collect_sub_objects
和在删除之前打破FK。
这是一个快速而又肮脏的例子:
from django.db.models.query import CollectedObjects
u = User.objects.get(id=1)
instances_to_be_deleted = CollectedObjects()
u._collect_sub_objects(instances_to_be_deleted)
for k in instances_to_be_deleted.ordered_keys():
inst_dict = instances_to_be_deleted.data[k]
for i in inst_dict.values():
i.sender = None # You will need a more generic way for this
i.save()
u.delete()
答案 1 :(得分:0)
我自己刚刚发现了ON DELETE CASCADE行为,我发现在Django 1.3中他们已经做出了外键行为configurable。