Django: ¿Obtener modelo de cadena?

133

En Django, puede especificar relaciones como:

author = ForeignKey('Person')

Y luego internamente tiene que convertir la cadena "Persona" en el modelo Person.

¿Dónde está la función que hace esto? Quiero usarlo, pero no puedo encontrarlo.

mpen
fuente

Respuestas:

167

A partir de Django 3.0, es AppConfig.get_model(model_name, require_ready=True)

A partir de Django 1.9, el método es django.apps.AppConfig.get_model(model_name).
- danihp

A partir de Django 1.7, django.db.models.loadingestá en desuso (se eliminará en 1.9) a favor del nuevo sistema de carga de aplicaciones.
- Scott Woodall


Lo encontré. Se define aquí:

from django.db.models.loading import get_model

Definido como:

def get_model(self, app_label, model_name, seed_cache=True):
mpen
fuente
11
Esta solución está en desuso en Django 1.7, consulte esta otra respuesta para la solución: stackoverflow.com/a/26126935/155987
Tim Saylor
44
Hola @mpen, te sugiero que actualices tu respuesta. En django 1.9 get_model se define en AppConfig :from django.apps import AppConfig
dani herrera
1
django.apps.AppConfig es un tipo generador que no puede funcionar como un objeto modelo.
Neeraj Kumar
2
En Django 1.7+ es mejor usar get_model () en el registro de la aplicación Django, que está disponible a través de django.apps.apps.get_model(model_name). Los objetos AppConfig están destinados a un propósito diferente y requieren que cree una instancia de AppConfig para poder llamar get_model().
zlovelady
2
Django 1.11 necesita la respuesta proporcionada por @luke_aus
Georg Zimmer
132

django.db.models.loadingfue desaprobado en Django 1.7 ( eliminado en 1.9 ) a favor del nuevo sistema de carga de aplicaciones .

Los documentos de Django 1.7 nos dan lo siguiente en su lugar:

>>> 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
fuente
Solía ​​usar la función "localizar" de pydoc ... no estoy seguro de la diferencia aquí, pero para permanecer dentro de Django he cambiado a este método. Gracias.
codificador de ira
61

solo para cualquiera que se quede atascado (como lo hice):

from django.apps import apps

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

app_namedebe aparecer entre comillas, como debería model_name(es decir, no intente importarlo)

get_model acepta minúsculas o mayúsculas 'model_name'

lukeaus
fuente
32

La mayoría de las "cadenas" del modelo aparecen como "appname.modelname", por lo que es posible que desee utilizar esta variación en get_model

from django.db.models.loading import get_model

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

La parte del código de Django que generalmente convierte tales cadenas en un modelo es un poco más compleja 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)

Para mí, este parece ser un buen caso para dividir esto en una sola función en el código central. Sin embargo, si sabe que sus cadenas están en formato "App.Model", las dos líneas anteriores funcionarán.

Ch'marr
fuente
3
Creo que la línea de segundo debe ser: your_model = get_model(*your_string.rsplit('.', 1)). La etiqueta de la aplicación a veces tiene formato punteado, sin embargo, el nombre del modelo siempre es un identificador válido.
Rockallite
1
Parece que esto no es necesario en el nuevo apps.get_model. "Como método abreviado, este método también acepta un único argumento en el formulario app_label.model_name".
Ryne Everett
16

La forma bendecida de hacer esto en Django 1.7+ es:

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

Entonces, en el ejemplo canónico de todos los tutoriales de framework:

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

En caso de que no sepa en qué aplicación existe su modelo, puede buscarlo de esta manera:

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

Recuerde que your_model_name debe estar en minúsculas.

Ola Nguyen Van
fuente
4

No estoy seguro de dónde se hace en Django, pero podrías hacer esto.

Asignación del nombre de la clase a la cadena mediante reflexión.

classes = [Person,Child,Parent]
def find_class(name):
 for clls in classes:
  if clls.__class__.__name__ == name:
   return clls
jbcurtin
fuente
1
¿Es clls .__ class __.__ name__ o simplemente clls .__ name__?
Vinayak Kaniyarakkal
4

Otra versión con menos código para los perezosos. Probado en Django 2+

from django.apps import apps
model = apps.get_model("appname.ModelName") # e.g "accounts.User"
Glicerina
fuente
1

Aquí hay un enfoque menos específico de django para obtener una clase de una cadena:

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

o puede usar importlib como se muestra aquí :

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