获取所有相关的Django模型对象

99
如何获取所有具有外键指向某个对象的模型对象列表?(类似于Django管理界面在DELETE CASCADE之前的删除确认页面)。
我正在尝试想出一种通用的方法来合并数据库中的重复对象。基本上,我希望所有具有外键指向对象"B"的对象都将被更新为指向对象"A",以便我可以删除"B"而不会丢失任何重要内容。
谢谢你的帮助!

1
这个Django片段绝对值得一看! - Nick Merrill
我正在尝试自己实现完全相同的东西。你愿意分享你的解决方案吗?特别是你是如何将相关对象设置为指向A的? - eugene
9个回答

94

Django <= 1.7

这会为你提供所有相关对象的属性名称:

links = [rel.get_accessor_name() for rel in a._meta.get_all_related_objects()]

然后,您可以使用以下内容获取所有相关对象:

for link in links:
    objects = getattr(a, link).all()
    for object in objects:
        # do something with related object instance

我花了一段时间试图弄清楚如何在我的模型中实现一种“观察者模式”。希望这对你有帮助。

Django 1.8+

使用_meta.get_fields()https://docs.djangoproject.com/en/1.10/ref/models/meta/#django.db.models.options.Options.get_fields (还可以在_get_fields()源代码中查看反向引用)


7
all() 方法在 OneToOneField 上会失败,你需要想办法检测它。 - augustomen
3
在Django 1.8+版本中,建议使用_meta.get_fields()替代它,因为这种方式不会显示多对多的连接并已被废弃。(在_get_fields()源代码中查看reverse):https://docs.djangoproject.com/en/1.10/ref/models/meta/#django.db.models.options.Options.get_fields - int_ua
1
感谢@int_ua的编辑!我很惊讶这个解决方法能够与Django保持兼容这么长时间。 - robbles
5
根据 _meta.get_fields() 的提示,以下是我解决问题的部分方案:links = [field.get_accessor_name() for field in obj._meta.get_fields() if issubclass(type(field), ForeignObjectRel)](假设导入了 from django.db.models.fields.related import ForeignObjectRel)。 - floer32

27
@digitalPBK 已经接近了答案……这里是使用 Django 内置的东西,它在 Django 管理界面中用于在删除相关对象时显示相关对象的内容。
from django.contrib.admin.utils import NestedObjects
collector = NestedObjects(using="default") #database name
collector.collect([objective]) #list of objects. single one won't do
print(collector.data)

这允许您创建Django管理员显示的内容 - 要被删除的相关对象。


根据我的了解,这似乎没有完全正常工作。它似乎遵循并不意味着删除标准的关系,尽管我不确定为什么会这样。 - Catskul

9

links = [rel.get_accessor_name() for rel in a._meta.get_all_related_objects()]

然后您可以使用以下方式来获取所有相关对象:

for link in links:
    objects = getattr(a, link.name).all()
    for object in objects:
        # do something with related object instance

来自Django 1.10官方文档:

MyModel._meta.get_all_related_objects() 现在是:

[
    f for f in MyModel._meta.get_fields()
    if (f.one_to_many or f.one_to_one)
    and f.auto_created and not f.concrete
]

因此,通过采用经过批准的示例,我们将使用以下内容:
links = [
            f for f in MyModel._meta.get_fields()
            if (f.one_to_many or f.one_to_one)
            and f.auto_created and not f.concrete
        ]

for link in links:
    objects = getattr(a, link.name).all()
    for object in objects:
        # do something with related object instance

只是稍微更正一下你的答案for link in links: objects = getattr(a, link.name).all() for object in objects: - Nam Ngo

7

尝试一下这个。

class A(models.Model):
    def get_foreign_fields(self):
      return [getattr(self, f.name) for f in self._meta.fields if type(f) == models.fields.related.ForeignKey]

不要忘记ManyToMany。 - theannouncer

7
以下是Django用于获取所有相关对象的代码:
from django.db.models.deletion import Collector
collector = Collector(using="default")
collector.collect([a])

print collector.data

1
似乎没有级联。我在这个特定的问题上还没有做足够的测试来知道完整的差异,但请参见我的答案以了解级联的情况。 - IMFletcher

6

Django 1.9
get_all_related_objects()已被弃用


(说明:该函数在Django 1.9中已被弃用,请使用相关的替代函数)
#Example: 
user = User.objects.get(id=1)
print(user._meta.get_fields())

注意: 已移除于Django110警告:'get_all_related_objects是一个非官方API,已被弃用。您可以尝试使用'get_fields()'

get_fields 不会返回相关的对象。 - iankit
它确实返回反向连接,即使在Django 1.8上也是如此,请参见https://docs.djangoproject.com/en/1.8/_modules/django/db/models/options/#Options.get_fields - int_ua

5
for link in links:
    objects = getattr(a, link).all()

对于相关集合(related sets)可以工作,但是对于外键(ForeignKeys)不行。由于RelatedManagers是动态创建的,因此查看类名比使用isinstance()更容易。

objOrMgr = getattr(a, link)
 if objOrMgr.__class__.__name__ ==  'RelatedManager':
      objects = objOrMgr.all()
 else:
      objects = [ objOrMgr ]
 for object in objects:
      # Do Stuff

2

很不幸,user._meta.get_fields() 只返回从 user 访问的关系,但是您可能有一些相关的对象,使用 related_name='+'。在这种情况下,该关系将不会由 user._meta.get_fields() 返回。因此,如果您需要通用且健壮的方法来合并对象,我建议使用上面提到的 Collector。


1
这里提供另一种获取相关模型字段列表(仅名称)的方法。
def get_related_model_fields(model):
    fields=[]
    related_models = model._meta.get_all_related_objects()
    for r in related_models:
        fields.extend(r.opts.get_all_field_names())
    return fields

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接