82

Say my_instance is of model MyModel.
I'm looking for a good way to do:

my_model = get_model_for_instance(my_instance)

I have not found any really direct way to do this. So far I have come up with this:

from django.db.models import get_model

my_model = get_model(my_instance._meta.app_label, my_instance.__class__.__name__)

Is this acceptable? Is it even a sure-fire, best practice way to do it?
There is also _meta.object_name which seems to deliver the same as __class__.__name__. Does it? Is better or worse? If so, why?

Also, how do I know I'm getting the correct model if the app label occurs multiple times within the scope of the project, e.g. 'auth' from 'django.contrib.auth' and let there also be 'myproject.auth'?
Would such a case make get_model unreliable?

Thanks for any hints/pointers and sharing of experience!

AnonJr
  • 2,759
  • 1
  • 26
  • 39
Geradeausanwalt
  • 2,040
  • 1
  • 17
  • 16
  • 3
    The `__class__` absolutely **must** be the class for the instance. There's no possible ambiguity. That's the way Python works. What are you asking? – S.Lott Jan 14 '10 at 13:56
  • Well, I have some limits of understanding here. To me, the ambiguity is that there could be more than one app label / class name combination, living in different modules. However, get_model doesn't receive the module as input. So which model will it deliver? The other thing is that I don't understand what object_name is good for, since it returns the same string as `__class__.__name__` does, and how many people choose which one of those two for usage in `get_model` or elsewhere. Thanks! – Geradeausanwalt Jan 14 '10 at 14:23

3 Answers3

128
my_model = type(my_instance)

To prove it, you can create another instance:

my_new_instance = type(my_instance)()

This is why there's no direct way of doing it, because python objects already have this feature.

updated...

I liked marcinn's response that uses type(x). This is identical to what the original answer used (x.__class__), but I prefer using functions over accessing magic attribtues. In this manner, I prefer using vars(x) to x.__dict__, len(x) to x.__len__ and so on.

updated 2...

For deferred instances (mentioned by @Cerin in comments) you can access the original class via instance._meta.proxy_for_model.

Community
  • 1
  • 1
Will Hardy
  • 14,588
  • 5
  • 44
  • 43
25
my_new_instance = type(my_instance)()
marcinn
  • 550
  • 4
  • 13
19

At least for Django 1.11, this should work (also for deferred instances):

def get_model_for_instance(instance):
    return instance._meta.model

Source here.

Rockallite
  • 16,437
  • 7
  • 54
  • 48
  • 3
    While this works, and I have unfortunately used this before, as far as "best practice" goes, `_meta.model` is not in the `_meta` [API](https://docs.djangoproject.com/en/2.2/ref/models/meta/), and shouldn't be used, `type()` is better and pythonic. – Andy Jan 23 '19 at 15:08