如何查找对实例的所有 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 时执行查询:
DO_NOTHING:不采取任何行动。如果您的数据库后端强制执行 参照完整性,这将导致 IntegrityError 除非您 手动将 SQL ON DELETE 约束添加到数据库字段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))
【讨论】:
以上是关于如何查找对实例的所有 Django 外键引用的主要内容,如果未能解决你的问题,请参考以下文章