Nullable ForeignKeys并删除引用的模型实例

时间:2009-03-25 13:06:42

标签: python django django-models

我有一个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处理程序。

有什么想法吗?我脑子里的结是哪里的?

2 个答案:

答案 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