【问题标题】:Nullable ForeignKeys and deleting a referenced model instanceNullable ForeignKeys 和删除引用的模型实例
【发布时间】:2010-10-15 10:54:03
【问题描述】:

我有一个 ForeignKey 在我的模型中可以为空,以模拟模型之间的松散耦合。看起来有点像:

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 处理程序。

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

【问题讨论】:

    标签: python django django-models


    【解决方案1】:

    我自己刚刚发现了 ON DELETE CASCADE 行为,我看到在 Django 1.3 中他们已经使外键行为configurable

    【讨论】:

      【解决方案2】:

      Django 确实模拟了 SQL 的 ON DELETE CASCADE 行为,并且没有开箱即用的记录方法来改变这一点。他们提到这一点的文档在本节末尾附近:Deleting objects

      你是对的,Django 收集所有相关的模型实例,然后为每个实例调用预删除处理程序。信号的发送者将是即将被删除的模型类,在这种情况下Message,而不是User,这使得很难检测到由用户触发的级联删除和普通删除之间的区别......特别是因为删除 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)
      
      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()
      

      【讨论】:

        猜你喜欢
        • 2016-12-13
        • 2020-10-29
        • 2013-01-07
        • 1970-01-01
        • 2016-12-12
        • 2016-03-18
        • 1970-01-01
        • 2016-02-05
        • 2011-03-20
        相关资源
        最近更新 更多