Get all related Django model objects

How can I get a list of all the model objects that have a ForeignKey pointing to an object? (Something like the delete confirmation page in the Django admin before DELETE CASCADE).

I’m trying to come up with a generic way of merging duplicate objects in the database. Basically I want all of the objects that have ForeignKeys points to object “B” to be updated to point to object “A” so I can then delete “B” without losing anything important.

Thanks for your help!

Answers:

Thank you for visiting the Q&A section on Magenaut. Please note that all the answers may not help you solve the issue immediately. So please treat them as advisements. If you found the post helpful (or not), leave a comment & I’ll get back to you as soon as possible.

Method 1

Django <= 1.7

This gives you the property names for all related objects:

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

You can then use something like this to get all related objects:

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

I spent a while trying to figure this out so I could implement a kind of “Observer Pattern” on
one of my models. Hope it’s helpful.

Django 1.8+

Use _meta.get_fields(): https://docs.djangoproject.com/en/1.10/ref/models/meta/#django.db.models.options.Options.get_fields (see reverse in the _get_fields() source also)

Method 2

@digitalPBK was close… here is probably what you are looking for using Django’s built-in stuff that is used in Django admin for displaying related objects during deletion

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)

this allows you to create what the Django admin displays – the related objects to be deleted.

Method 3

Give this a try.

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]

Method 4

The following is what django uses to get all related objects

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

print collector.data

Method 5

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

You can then use something like this to get all related objects:

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

From Django 1.10 offical docs:

MyModel._meta.get_all_related_objects() becomes:

[
    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
]

So by taking the approved example we would use:

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

Method 6

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

Works for related sets, but not for ForeignKeys. Since RelatedManagers are created dynamically, looking at the class name is easier than doing an isinstance()

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

Method 7

Django 1.9
get_all_related_objects() has been deprecated

#Example: 
user = User.objects.get(id=1)
print(user._meta.get_fields())

Note:
RemovedInDjango110Warning: ‘get_all_related_objects is an unofficial API that has been deprecated. You may be able to replace it with ‘get_fields()’

Method 8

Unfortunately, user._meta.get_fields() returns only relations accessible from user, however, you may have some related object, which uses related_name=’+’. In such case, the relation would not be returned by user._meta.get_fields(). Therefore, if You need generic and robust way to merge objects, I’d suggest to use the Collector mentioned above.

Method 9

Here is another way to get a list of fields (names only) in related models.

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


All methods was sourced from stackoverflow.com or stackexchange.com, is licensed under cc by-sa 2.5, cc by-sa 3.0 and cc by-sa 4.0

0 0 votes
Article Rating
Subscribe
Notify of
guest

0 Comments
Oldest
Newest Most Voted
Inline Feedbacks
View all comments
0
Would love your thoughts, please comment.x
()
x