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


88

如何获得所有具有ForeignKey指向对象的模型对象的列表?(有点像Django管理员中DELETE CASCADE之前的Delete确认页面)。

我试图提出一种合并数据库中重复对象的通用方法。基本上,我希望所有具有ForeignKeys指向对象“ B”的对象都被更新为指向对象“ A”,这样我就可以删除“ B”而不会丢失任何重要内容。

谢谢你的帮助!


1
这个Django片段绝对值得一试!
Nick Merrill

我正在尝试自己实现完全相同的事情。您愿意分享您的解决方案吗?特别set是相关对象如何指向A?
eugene

Answers:


84

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,2012年

2
它不显示ManyToMany连接并且已弃用。在Django 1.8+它的建议被替换为_meta.get_fields()docs.djangoproject.com/en/1.10/ref/models/meta/...(见reverse_get_fields()人士还)
int_ua

1
感谢您编辑@int_ua!令我惊讶的是,这种解决方法只要与Django兼容,就一直兼容。
失速

4
_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
漂流者

20

@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管理员显示的内容-要删除的相关对象。


FWICT不能正常工作。尽管我不确定为什么,但似乎遵循的关系并不意味着删除标准。
Catskul

1
是我还是Collector不使用(从第1行导入)?
djvg

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
播音员

6

以下是django用于获取所有相关对象的内容

from django.db.models.deletion import Collector
collector = Collector(using="default")
collector.collect([a])

print collector.data

1
似乎没有级联。尚未针对这一特定主题进行足够的测试以了解全部差异,但请参见我对于级联的答案。
IMFletcher

6

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

只是为了纠正你的答案有点 python for link in links: objects = getattr(a, link.name).all() for object in objects:
南鹤

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

适用于相关集,但不适用于ForeignKeys。由于RelatedManager是动态创建的,因此查看类名比执行isinstance()容易。

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


2

这是获取相关模型中的字段列表(仅名称)的另一种方法。

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

2

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

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.