0
model_name = "MyModel" #This is coming from a different input, it can be any different string name not necessarily "MyModel"

model_name.objects.filter()

AttributeError: 'str' object has no attribute 'objects'

How can I make this work, I have already tried:

exec(model_name)
input(model_name)
[model_name]

I know there is a possible method where you import the model using apps.get_model(model_name="string") but I don't want to use this method due to the model already being imported beforehand. Is there any different way where I can convert model_name (string type) to a type that can be inserted at INSERTHERE.objects.filter()

Update: I know an easier implementation would be to use if + elif statements to query the correct model. I'm trying to find a way to avoid using if + 100 elif statements..

  • What do you mean by "due to the model already being imported beforehand"? – ruddra Apr 05 '23 at 13:00
  • Why not you just `import MyModel` – veekxt Apr 05 '23 at 13:07
  • @veekxt because its coming from elsewhere as string... – CODComputerAtWar Apr 05 '23 at 13:56
  • @ruddra I already have the model being imported in the file at the top. I Mean: import MyModel1, MyModel2, MyModel3.. is already mentioned. Further details: ```User input => object = CODComputer.objects.filter(name=User Input) => model_name = object.saved_name # this is a string type => model_name.objects.filter() #this is what am trying to achieve.``` – CODComputerAtWar Apr 05 '23 at 14:08
  • @CODComputerAtWar If I understand correctly, you save model names as strings in a field in the `CODComputer` model and you want to take that name and get the actual model class so you can run queries? Can you share the model containing the model names and your view where you take the user input? It may be easier to use the contenttypes framework to store these relationships – Iain Shelvington Apr 05 '23 at 14:20
  • @IainShelvington a very easy implementation would be: ```User input => object = CODComputer.objects.filter(name=User Input) => model_name = object.saved_name => if model_name == 'ModelName1': ModelName1.objects.filter() elif model_name == 'ModelName2: ModelName2.objects.filter()``` but that will result in a big amount of elif statements as every possible return from object.saved_name will have to be considered and implemented in the elif statements (am trying to avoid). Any better way without having to use ```apps.get_model(model_name="string")``` example? – CODComputerAtWar Apr 05 '23 at 18:35

2 Answers2

1

As you mentioned, if you want to use .objects you will need to get the Model object first.

You can do so using either :

from myapp.models import MyModel

MyModel.objects.all() # This works !

Or starting from a string

from django.apps import apps

MyModel = apps.get_model(app_label='myapp', model_name='MyModel')

MyModel.objects.all() # This works !

The model being imported beforehand is no issue, it's actually a good thing that allows you to use the first snippet I wrote.

When you use the dot notation, eg foo.bar you try to access the bar property from the foo object.

In django, the Model class (from django.db.models) has a property named objects that has methods (=functions) such as filter(), all() etc.

You want to get a Model object and access its objects property to talk to your database (for example, using filter())

When you do "MyModel".objects.all() you try to access to the objects property of a string object, not from a Model object, this is why it doesn't work that way.

  • The first part is not what I am looking for. If I have a string coming from elsewhere is the question.. Second part is not any good because I have already mentioned its possible to do but I want to use a different method. – CODComputerAtWar Apr 05 '23 at 13:57
  • Can I ask why you want to use a different method ? If you want to use a string, the 2nd snippet is the perfect solution, it allows you to get a model from its name using a string. You can name the variable however you want, the important parts are the arguments. – Benjamin_Mourgues Apr 06 '23 at 07:36
1

A string can't be a class directly. In your case, you can use a string to Model class map to directly query the information, like this:

s2m_map = {'modelA': ModelA, 'modelB': ModelB, ...}

# OR

from django.apps import apps

s2m_map = {
    model.__name__: model for model in apps.get_models()
}
# based on the comment in your question
objects = CODComputer.objects.filter(name='User Input')
for obj in objects:
     model_name = object.saved_name
     s2m_map[model_name].objects.all()

(But) If model_name was not a string, instead a ForeignKey relation, then you could have used the following code:

model_name = object.saved_name.__class__
model_name.objects.all()

Alternatively, you can look into Generic ForeignKey for supporting models of different classes to be directly used (instead of string).

ruddra
  • 50,746
  • 7
  • 78
  • 101
  • 1
    First option is not any good for me because it will be similar to using if & elif statements.. Hmmm thanks for pointing out the Foreign Key method I totally missed that. I think the foreign key method would be the best solution in this case (Faster than other methods), but I am still curious If its possible to convert a defined string in order to be placed as a model name in a query statement ```model_name = "AbcdModel" => model_name.objects.filter()``` – CODComputerAtWar Apr 05 '23 at 18:50