Django: Obtenir le modèle à partir de la chaîne?

133

Dans Django, vous pouvez spécifier des relations telles que:

author = ForeignKey('Person')

Et puis en interne, il doit convertir la chaîne "Person" dans le modèle Person.

Où est la fonction qui fait cela? Je veux l'utiliser, mais je ne le trouve pas.

mpen
la source

Réponses:

167

Depuis Django 3.0, c'est AppConfig.get_model(model_name, require_ready=True)

Depuis Django 1.9, la méthode est django.apps.AppConfig.get_model(model_name).
- danihp

Depuis Django 1.7, le django.db.models.loadingest obsolète (à supprimer dans la version 1.9) au profit du nouveau système de chargement d'application.
- Scott Woodall


Je l'ai trouvé. Il est défini ici:

from django.db.models.loading import get_model

Défini comme:

def get_model(self, app_label, model_name, seed_cache=True):
mpen
la source
11
Cette solution est obsolète dans Django 1.7, voir cette autre réponse pour la solution: stackoverflow.com/a/26126935/155987
Tim Saylor
4
Bonjour @mpen, je vous suggère de mettre à jour votre réponse. Sur django 1.9, get_model est défini sur AppConfig :from django.apps import AppConfig
dani herrera
1
django.apps.AppConfig est un type de générateur qui ne peut pas fonctionner comme un objet modèle.
Neeraj Kumar
2
Dans Django 1.7+, il est préférable d'utiliser get_model () sur le registre de l'application Django, qui est disponible via django.apps.apps.get_model(model_name). Les objets AppConfig sont destinés à un objectif différent et vous obligent à créer une instance AppConfig pour pouvoir appeler get_model().
zlovelady
2
Django 1.11 a besoin de la réponse fournie par @luke_aus
Georg Zimmer
132

django.db.models.loadinga été déconseillé dans Django 1.7 ( supprimé dans 1.9 ) au profit du nouveau système de chargement d'application .

La documentation Django 1.7 nous donne plutôt ce qui suit:

>>> from django.apps import apps
>>> User = apps.get_model(app_label='auth', model_name='User')
>>> print(User)
<class 'django.contrib.auth.models.User'>
Scott Woodall
la source
J'avais l'habitude d'utiliser la fonction "Locate" de pydoc ... je ne suis pas sûr de la différence ici, mais pour rester dans Django, je suis passé à cette méthode. Merci.
warath-coder
61

juste pour tous ceux qui restent coincés (comme moi):

from django.apps import apps

model = apps.get_model('app_name', 'model_name')

app_namedoit être répertorié avec des guillemets, comme il se doit model_name(c'est-à-dire n'essayez pas de l'importer)

get_model accepte les minuscules ou les majuscules 'model_name'

lukeaus
la source
32

La plupart des "chaînes" de modèle apparaissent sous la forme "appname.modelname", vous pouvez donc utiliser cette variante sur get_model

from django.db.models.loading import get_model

your_model = get_model ( *your_string.split('.',1) )

La partie du code django qui transforme généralement ces chaînes en un modèle est un peu plus complexe à partir de django/db/models/fields/related.py:

    try:
        app_label, model_name = relation.split(".")
    except ValueError:
        # If we can't split, assume a model in current app
        app_label = cls._meta.app_label
        model_name = relation
    except AttributeError:
        # If it doesn't have a split it's actually a model class
        app_label = relation._meta.app_label
        model_name = relation._meta.object_name

# Try to look up the related model, and if it's already loaded resolve the
# string right away. If get_model returns None, it means that the related
# model isn't loaded yet, so we need to pend the relation until the class
# is prepared.
model = get_model(app_label, model_name,
                  seed_cache=False, only_installed=False)

Pour moi, cela semble être un bon cas pour diviser cela en une seule fonction dans le code de base. Cependant, si vous savez que vos chaînes sont au format «App.Model», les deux lignes ci-dessus fonctionneront.

Ch'marr
la source
3
Je pense que la ligne 2 devrait être: your_model = get_model(*your_string.rsplit('.', 1)). L'étiquette d'application est parfois au format pointillé, mais le nom du modèle est toujours un identifiant valide.
Rockallite
1
On dirait que ce n'est pas nécessaire dans le nouveau apps.get_model. "En tant que raccourci, cette méthode accepte également un seul argument dans le formulaire app_label.model_name."
Ryne Everett
16

La meilleure façon de faire cela dans Django 1.7+ est:

import django
model_cls = django.apps.apps.get_model('app_name', 'model_name')

Donc, dans l'exemple canonique de tous les tutoriels de framework:

import django
entry_cls = django.apps.apps.get_model('blog', 'entry')  # Case insensitive
Craig Labenz
la source
9

Si vous ne savez pas dans quelle application votre modèle existe, vous pouvez le rechercher de cette façon:

from django.contrib.contenttypes.models import ContentType 
ct = ContentType.objects.get(model='your_model_name') 
model = ct.model_class()

N'oubliez pas que votre_nom_modèle doit être en minuscules.

Ola Nguyen Van
la source
4

Je ne sais pas où c'est fait dans Django, mais vous pouvez le faire.

Mappage du nom de la classe sur la chaîne via la réflexion.

classes = [Person,Child,Parent]
def find_class(name):
 for clls in classes:
  if clls.__class__.__name__ == name:
   return clls
jbcurtin
la source
1
Est-ce clls .__ class __.__ name__ ou simplement clls .__ name__?
Vinayak Kaniyarakkal
4

Un autre rendu avec moins de code pour les paresseux. Testé dans Django 2+

from django.apps import apps
model = apps.get_model("appname.ModelName") # e.g "accounts.User"
Glycérine
la source
1

Voici une approche moins spécifique à django pour obtenir une classe à partir de string:

mymodels = ['ModelA', 'ModelB']
model_list = __import__('<appname>.models', fromlist=mymodels)
model_a = getattr(model_list, 'ModelA')

ou vous pouvez utiliser importlib comme indiqué ici :

import importlib
myapp_models = importlib.import_module('<appname>.models')
model_a = getattr(myapp_models, 'ModelA')
Schubisu
la source