Nullable ForeignKeys并删除引用的模型实例 [英] Nullable ForeignKeys and deleting a referenced model instance

查看:105
本文介绍了Nullable ForeignKeys并删除引用的模型实例的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个ForeignKey,在我的模型中可以为null来模拟模型之间的松耦合。它看起来像这样:

  class Message(models.Model):
sender = models.ForeignKey(User, null = True,blank = True)
sender_name = models.CharField(max_length = 255)

保存发件人名称写入sender_name属性。现在,我想删除发件人引用的用户实例,并将消息留在原位。



开箱即用,此代码总是导致已删除的邮件一旦我删除了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收集所有相关的模型实例,然后为每个实例调用预删除处理程序。信号的发送者将是要删除的模型类,在这种情况下消息而不是用户这使得很难检测到由用户触发的级联删除与正常删除之间的区别...特别是因为删除User类的信号来自最后,因为这是最后一个删除: - )



然而,您可以在调用User.delete()函数之前获取Django建议删除的对象列表。每个模型实例都有一个称为 _collect_sub_objects()的半私有方法,它编译具有指向它的外键的实例列表(它编译此列表而不删除实例)。您可以在 django.db.base 中查看 delete()来查看该方法的调用方式。 >

如果这是您自己的对象之一,我建议您覆盖实例上的 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)

在instances_to_be_deleted.ordered_keys()中:
inst_dict = instances_to_be_deleted.data [k]
for i在inst_dict.values()中:
i.sender = None#您将需要一个更通用的方式为
i.save()

u.delete()


I have a ForeignKey which can be null in my model to model a loose coupling between the models. It looks somewhat like that:

class Message(models.Model):
  sender = models.ForeignKey(User, null=True, blank=True)
  sender_name = models.CharField(max_length=255)

On save the senders name is written to the sender_name attribute. Now, I want to be able to delete the User instance referenced by the sender and leave the message in place.

Out of the box, this code always results in deleted messages as soon as I delete the User instance. So I thought a signal handler would be a good idea.

def my_signal_handler(sender, instance, **kwargs):
  instance.message_set.clear()
pre_delete.connect(my_signal_handler, sender=User)

Sadly, it is by no means a solution. Somehow Django first collects what it wants to delete and then fires the pre_delete handler.

Any ideas? Where is the knot in my brain?

解决方案

Django does indeed emulate SQL's ON DELETE CASCADE behaviour, and there's no out-of-the box documented way to change this. The docs where they mention this are near the end of this section: Deleting objects.

You are right that Django's collects all related model instances, then calls the pre-delete handler for each. The sender of the signal will be the model class about to be deleted, in this case Message, rather than User, which makes it hard to detect the difference between a cascade delete triggered by User and a normal delete... especially since the signal for deleting the User class comes last, since that's the last deletion :-)

You can, however, get the list of objects that Django is proposing to delete in advance of calling the User.delete() function. Each model instance has a semi-private method called _collect_sub_objects() that compiles the list of instances with foreign keys pointing to it (it compiles this list without deleting the instances). You can see how this method is called by looking at delete() in django.db.base.

If this was one of your own objects, I'd recommend overriding the delete() method on your instance to run _collect_sub_objects(), and then break the ForeignKeys before calling the super class delete. Since you're using a built-in Django object that you may find too difficult to subclass (though it is possible to substitute your own User object for django's), you may have to rely on view logic to run _collect_sub_objects and break the FKs before deletion.

Here's a quick-and-dirty example:

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()

这篇关于Nullable ForeignKeys并删除引用的模型实例的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆