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

时间:2015-10-29 20:52:29

标签: python django

如何找到对特定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()

然而,这似乎有两个问题:

  1. 有时collector.field_updates包含对与我的目标obj_to_delete无关的模型和字段的引用。
  2. 我的最终obj_to_delete.delete()调用失败,IntegrityErrors抱怨仍然引用它的剩余记录,收集器未捕获的记录。
  3. 我做错了什么?

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

2 个答案:

答案 0 :(得分:2)

您可以使用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

答案 1 :(得分: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:设置ForeignKey为null;这只有在null的情况下才有可能 真。

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

  • SET():将ForeignKey设置为传递给SET()的值,或者如果是 传入callable,调用它的结果。在大多数情况下,为了避免在导入models.py时执行查询,必须传递一个callable:

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约束添加到数据库字段