nehem
nehem

Reputation: 13642

Django get the Model name using ORM's related field name

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

Answers (3)

knbk
knbk

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

nik_m
nik_m

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

itzMEonTV
itzMEonTV

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

Related Questions