'Django: list all reverse relations of a model
I would like my django application to serve a list of any model's fields (this will help the GUI build itself).
Imagine the classes (ignore the fact that all field of Steps
could be in Item
, I have my reasons :-) )
class Item(models.Model):
name = models.CharField(max_length=100)
description = models.TextField()
class Steps(models.Model):
item = models.OneToOneField('Item', related_name='steps')
design = models.BooleanField(default=False)
prototype = models.BooleanField(default=False)
production = models.BooleanField(default=False)
Now, when I want to list a model's fields:
def get_fields(model):
return model._meta.fields + model._meta.many_to_many
But I would also like to get the list of "related" one-to-one foreign keys to my models. In my case Item.steps
would not be in that list.
I have found that model._meta.get_all_field_names
does include all the related fields.
But when I call Item._meta.get_field_by_name('steps')
it returns a tuple holding a RelatedObject
, which does not tell me instantly whether this is a single relation or a one-to-many (I want to list only reversed one-to-one relations).
Also, I can use this bit of code:
from django.db.models.fields.related import SingleRelatedObjectDescriptor
reversed_f_keys = [attr for attr in Item.__dict__.values() \
if isinstance(attr, SingleRelatedObjectDescriptor)]
But I'm not very satisfied with this.
Any help, idea, tips are welcome!
Cheers
Solution 1:[1]
This was changed (in 1.8 I think) and Olivier's answer doesn't work anymore. According to the docs, the new way is
[f for f in Item._meta.get_fields()
if f.auto_created and not f.concrete]
This includes one-to-one, many-to-one, and many-to-many.
Solution 2:[2]
I've found out that there are methods of Model._meta
that can give me what I want.
my_model = get_model('app_name','model_name')
# Reverse foreign key relations
reverse_fks = my_model._meta.get_all_related_objects()
# Reverse M2M relations
reverse_m2ms = my_model._meta.get_all_related_many_to_many_objects()
By parsing the content of the relations, I can guess whether the "direct" field was a OneToOneField
or whatever.
Solution 3:[3]
And what about this :
oneToOneFieldNames = [
field_name
for field_name in Item._meta.get_all_field_names()
if isinstance(
getattr(
Item._meta.get_field_by_name(field_name)[0],
'field',
None
),
models.OneToOneField
)
]
RelatedObject may have a Field attribute for relations. You just have to check if this is a OneToOne field and you can retrieve only what you want
Solution 4:[4]
I was looking into this answer as a starting point to identify reversed relationships for a model instance.
So, I noticed that when you get all the fields using instance._meta.get_fields()
, those that are direct relationships, which are 3 types (ForeignKey
, ManyToMany
, OneTone
), their parent class (field.__class__.__bases__
) is django.db.models.fields.related.ForeignKey
.
However, those that are reverse relationships inherit from django.db.models.fields.reverse_related.ForeignObjectRel
. And if you take a look at this class, it has:
auto_created = True
concrete = False
So you could identify those by the attributes mentioned in the top-rated answer or by asking isinstance(field, ForeignObjectRel
.
Another thing I could notice is that those reverse relationships have a field
attribute which points to the direct relationship generating that reverse relationship.
Additionally, in order to exclude the fields instantiating the through
table, those have through
and through_fields
attributes
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
Solution | Source |
---|---|
Solution 1 | Lutz Prechelt |
Solution 2 | Olivier H |
Solution 3 | DylannCordel |
Solution 4 | Jaime Escobar |