Reputation: 13642
I have a string like this order__product__category__description
which is a related expression of my Django's model structure
Now I have a model called Shipment
field = 'order__product__category__description'
Here description
is a column name of table/model Category
. Here comes the question, just by having this model Shipment
and this field string order__product__category__description
how do I find these models Order
, Product
, Category
.
My use-case is I need to store all the field_names of Category
in a list. Any idea on how to connect the dots? having left with just two details Shipment
& that field string.
First thing comes to mind is to split by __
and to come up with a list like this ['order','product','category']
and to iterate the model _meta
based on the field name. Any other elegant ways would be appreciated.
Upvotes: 1
Views: 2245
Reputation: 53669
If you want to get the related model from the model class (rather than the related instance from a model instance), you can use _meta.get_field()
combined with field.related_model
to get the related model class:
from django.core.exceptions import FieldDoesNotExist
model = Shipment
lookup = 'order__product__category__description'
for name in lookup.split('__'):
try:
field = model._meta.get_field(name)
except FieldDoesNotExist:
# name is probably a lookup or transform such as __contains
break
if hasattr(field, 'related_model'):
# field is a relation
model = field.related_model
else:
# field is not a relation, any name that follows is
# probably a lookup or transform
break
Then do with model
what you want.
Upvotes: 5
Reputation: 12086
Assuming you have split the string it into a list, as you said to these:
models = ['order', 'product', 'category']
First get the app label (a string) that each model refers to:
from django.contrib.contenttypes.models import ContentType
app_labels = []
for model in models:
app_labels.append(ContentType.objects.filter(model=model).values('app_label').first().get('app_label'))
Now you have both model
names and app
names. So:
from django.apps import apps
model_classes = []
for app, model in zip(app_labels, models):
model_classes.append(apps.get_model(app, model))
Finally, get each model fields with the _meta
attribute like you already know:
for model in model_classes:
print(model._meta.get_fields())
Upvotes: 0
Reputation: 20339
I didn't understand well. Anyway hope this you want.
field = 'order__product__category__description'
To get product
from Shipment
instance
product_var = ".".join(field.split("__")[:2])
Then
from operator import attrgetter
attrgetter(product_var)(shipment_instance)
Also you can get all related as a tuple
attrgetter(".".join(field.split("__")[:1]), ".".join(field.split("__")[:2]), ".".join(field.split("__")[:3]), ".".join(field.split("__")[:4]))(shipment_instance)
Hope this helps.
Upvotes: 1