我有一个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处理程序.
有任何想法吗?我脑子里的结是哪里的?
Django确实模仿了SQL的ON DELETE CASCADE
行为,并且没有开箱即用的文档来改变它.他们提到这个的文档接近本节的结尾:删除对象.
你是对的,Django收集所有相关的模型实例,然后为每个实例调用预删除处理程序.信号的发送者将是要删除的模型类,在这种情况下Message
,而不是User
,这使得很难检测到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()