如何找到对特定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的标准删除视图中那样使用任何类似的依赖查找。
答案 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))