如何查找对实例的所有 Django 外键引用

Posted

技术标签:

【中文标题】如何查找对实例的所有 Django 外键引用【英文标题】:How to find all Django foreign key references to an instance 【发布时间】:2015-10-29 20:52:28 【问题描述】:

如何找到对特定 Django 模型实例的所有直接外键引用?

我想删除一条记录,但我想保留所有引用它的子记录,所以我试图在删除之前用另一个记录“交换”对旧记录的引用。

This 类似的问题引用了 Collector 类。我试过了:

obj_to_delete = MyModel.objects.get(id=blah)
new_obj = MyModel.objects.get(id=blah2)
collector = Collector(using='default')
collector.collect([obj_to_delete])
for other_model, other_data in collector.field_updates.iteritems():
    for (other_field, _value), other_instances in other_data.iteritems():

        # Why is this necessary?
        if other_field.rel.to is not type(first_obj):
            continue

        for other_instance in other_instances:
            setattr(other_instance, other_field.name, new_obj)
            other_instance.save()

# All FK references should be gone, so this should be safe to delete.
obj_to_delete.delete()

不过,这似乎有两个问题:

    有时collector.field_updates 包含对与我的目标obj_to_delete 无关的模型和字段的引用。 我的最后一次 obj_to_delete.delete() 调用失败,IntegrityErrors 抱怨剩余的记录仍然引用它,这些记录未被收集器捕获。

我做错了什么?

我只需要一种方法来查找对单个模型实例的所有 FK 引用。我不需要像 Django 的标准删除视图中使用的任何花哨的依赖项查找。

【问题讨论】:

【参考方案1】:

您可以使用 Django 的反向外键支持。

假设你有两个模型,像这样:

class Foo(models.Model):
   name = models.CharField(max_length=10)

class Bar(models.Model):
   descr = models.CharField(max_length=100)
   foo = models.ForeignKey(Foo)

然后你知道你可以使用bar_instance.foo 来访问它所指向的 Foo 对象。但是您可以在Foo 实例上使用反向外键来获取所有指向它的Bar 对象,例如foo.bar_set

【讨论】:

【参考方案2】:

我个人认为最好的选择是避免级联删除。

使用适当的 Django 选项在相关模型中声明外键,例如

on_delete=models.SET_NULL

应该够了。

从@Joseph 的回答中借用示例模型:

class Foo(models.Model):
   name = models.CharField(max_length=10)

class Bar(models.Model):
   descr = models.CharField(max_length=100)
   foo = models.ForeignKey(Foo, blank=True, null=True, on_delete=models.SET_NULL))

正如官方Django docs 中所述,以下是您可以使用和试验的预定义行为:

SET_NULL:设置外键为空;仅当 null 为 没错。

SET_DEFAULT:将 ForeignKey 设置为其默认值;默认为 必须设置 ForeignKey。

SET():将 ForeignKey 设置为传递给 SET() 的值,或者如果 callable 被传入,调用它的结果。在大多数情况下,需要传递一个可调用对象以避免在导入 models.py 时执行查询:

from django.conf import settings
from django.contrib.auth import get_user_model
from django.db import models

def get_sentinel_user():
    return get_user_model().objects.get_or_create(username='deleted')[0]

class MyModel(models.Model):
    user = models.ForeignKey(settings.AUTH_USER_MODEL,
                             on_delete=models.SET(get_sentinel_user))
DO_NOTHING:不采取任何行动。如果您的数据库后端强制执行 参照完整性,这将导致 IntegrityError 除非您 手动将 SQL ON DELETE 约束添加到数据库字段

【讨论】:

以上是关于如何查找对实例的所有 Django 外键引用的主要内容,如果未能解决你的问题,请参考以下文章

SQL Server查找一个外键被引用的所有表

Django推迟外键查找

Django 啥时候查找外键的主键?

Django 反向查找外键

Django外键反向查找

带有查找字段的嵌套模型外键上的 Django JOIN