django: Best-Practice-Methode zum Abrufen eines Modells aus einer Instanz dieses Modells

69

Sprich my_instanceist vom Modell MyModel.
Ich suche nach einem guten Weg:

my_model = get_model_for_instance(my_instance)

Ich habe keinen wirklich direkten Weg gefunden, dies zu tun. Bisher habe ich mir Folgendes ausgedacht:

from django.db.models import get_model

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

Ist das akzeptabel? Ist es überhaupt ein sicherer, bewährter Weg, dies zu tun?
Es gibt auch _meta.object_namedas, was das gleiche zu liefern scheint wie __class__.__name__. Macht es? Ist besser oder schlechter? Wenn ja warum?

Woher weiß ich auch, dass ich das richtige Modell erhalte, wenn das App-Label im Rahmen des Projekts mehrmals vorkommt, z. B. 'auth' von 'django.contrib.auth', und dass es auch 'myproject.auth' gibt?
Würde ein solcher Fall get_modelunzuverlässig machen ?

Vielen Dank für Hinweise / Hinweise und Erfahrungsaustausch!

Geradeausanwalt
quelle
3
Das muss__class__ unbedingt die Klasse für die Instanz sein. Es gibt keine mögliche Mehrdeutigkeit. So funktioniert Python. Was fragst du?
S.Lott
Nun, ich habe hier einige Grenzen des Verständnisses. Für mich besteht die Unklarheit darin, dass es mehr als eine Kombination aus App-Label und Klassenname geben kann, die in verschiedenen Modulen leben. Get_model empfängt das Modul jedoch nicht als Eingabe. Welches Modell wird es also liefern? Die andere Sache ist, dass ich nicht verstehe, wofür Objektname gut ist, da er dieselbe Zeichenfolge zurückgibt wie __class__.__name__und wie viele Personen auswählen, welche dieser beiden für die Verwendung in get_modeloder anderswo verwendet werden soll. Vielen Dank!
Geradeausanwalt

Antworten:

113
my_model = type(my_instance)

Um dies zu beweisen, können Sie eine andere Instanz erstellen:

my_new_instance = type(my_instance)()

Aus diesem Grund gibt es keine direkte Möglichkeit, da Python-Objekte diese Funktion bereits haben.

Aktualisiert...

Ich mochte Marcinns Antwort , die verwendet type(x). Dies ist identisch mit der ursprünglichen Antwort ( x.__class__), aber ich bevorzuge die Verwendung von Funktionen gegenüber dem Zugriff auf magische Attribute. Auf diese Weise benutze ich lieber vars(x)to x.__dict__, len(x)to x.__len__und so weiter.

aktualisiert 2 ...

Für verzögerte Instanzen (von @Cerin in Kommentaren erwähnt) können Sie über auf die ursprüngliche Klasse zugreifen instance._meta.proxy_for_model.

Will Hardy
quelle
3
Dies funktioniert nicht mit verzögerten Instanzen oder Instanzen aus Abfragen mit only().
Cerin
Vielen Dank für die 'aktualisierte 2'. Ich habe ewig gesucht, wie ich auf die ursprüngliche Klasse zugreifen kann.
andreibosco
23
my_new_instance = type(my_instance)()
marcinn
quelle
9

Zumindest für Django 1.11 sollte dies funktionieren (auch für verzögerte Instanzen):

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

Quelle hier .

Rockallite
quelle
2
Während dies funktioniert und ich dies leider schon einmal verwendet habe, ist, soweit "Best Practice" geht, _meta.modelnicht in der _meta API enthalten und sollte nicht verwendet werden, type()ist besser und pythonisch.
Andy