Django: Pobierz model ze stringa?

137

W Django możesz określić relacje takie jak:

author = ForeignKey('Person')

Następnie wewnętrznie musi przekształcić łańcuch „Osoba” w model Person.

Gdzie jest funkcja, która to robi? Chcę go użyć, ale nie mogę go znaleźć.

mpen
źródło

Odpowiedzi:

174

Od Django 3.0 to AppConfig.get_model(model_name, require_ready=True)

Od wersji Django 1.9 metoda to django.apps.AppConfig.get_model(model_name).
- danihp

Począwszy od Django 1.7, django.db.models.loadingjest on przestarzały (ma zostać usunięty w 1.9) na rzecz nowego systemu ładowania aplikacji.
- Scott Woodall


Znalazłem to. Jest zdefiniowane tutaj:

from django.db.models.loading import get_model

Zdefiniowana jako:

def get_model(self, app_label, model_name, seed_cache=True):
mpen
źródło
12
To rozwiązanie jest przestarzałe w Django 1.7, zobacz inną odpowiedź na rozwiązanie: stackoverflow.com/a/26126935/155987
Tim Saylor
4
Cześć @mpen, proponuję zaktualizować swoją odpowiedź. W django 1.9 get_model jest zdefiniowane na AppConfig :from django.apps import AppConfig
dani herrera
1
django.apps.AppConfig to typ generatora, który nie może działać jak obiekt modelu.
Neeraj Kumar
2
W Django 1.7+ lepiej jest używać get_model () w rejestrze aplikacji Django, który jest dostępny poprzez django.apps.apps.get_model(model_name). Obiekty AppConfig są przeznaczone do innego celu i wymagają utworzenia wystąpienia AppConfig w celu wywołania get_model().
zlovelady
2
Django 1.11 potrzebuje odpowiedzi udzielonej przez @luke_aus
Georg Zimmer
136

django.db.models.loadingzostał przestarzały w Django 1.7 ( usunięty w 1.9 ) na rzecz nowego systemu ładowania aplikacji .

Zamiast tego dokumentacja Django 1.7 zawiera następujące informacje:

>>> 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
źródło
Kiedyś korzystałem z funkcji "locate" pydoc ... nie jestem pewien różnicy, ale aby pozostać w Django, przełączyłem się na tę metodę. Dzięki.
warath-coder
61

tylko dla każdego, kto utknął (tak jak ja):

from django.apps import apps

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

app_namepowinny być wymienione w cudzysłowach, tak jak powinno model_name(tj. nie próbuj ich importować)

get_model akceptuje małe lub duże litery „nazwa_modelu”

Lukeaus
źródło
33

Większość „ciągów” modelu ma postać „nazwa_aplikacji.modelname”, więc możesz chcieć użyć tej odmiany w get_model

from django.db.models.loading import get_model

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

Część kodu django, która zwykle zamienia takie łańcuchy w model, jest trochę bardziej złożona 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)

Wydaje mi się, że jest to dobry przypadek do podzielenia tego na jedną funkcję w kodzie podstawowym. Jeśli jednak wiesz, że Twoje ciągi znaków są w formacie „App.Model”, powyższe dwie linijki będą działać.

Ch'marr
źródło
3
Myślę, że linia 2-szy powinno być: your_model = get_model(*your_string.rsplit('.', 1)). Etykieta aplikacji ma czasem format kropkowy, jednak nazwa modelu jest zawsze prawidłowym identyfikatorem.
Rockallite
1
Wygląda na to, że w nowej wersji nie jest to konieczne apps.get_model. „W skrócie ta metoda akceptuje również pojedynczy argument w formularzu app_label.model_name”.
Ryne Everett
16

Błogosławionym sposobem na to w Django 1.7+ jest:

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

Tak więc w kanonicznym przykładzie wszystkich samouczków dotyczących frameworka:

import django
entry_cls = django.apps.apps.get_model('blog', 'entry')  # Case insensitive
Craig Labenz
źródło
9

Jeśli nie wiesz, w której aplikacji istnieje Twój model, możesz przeszukać go w ten sposób:

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

Pamiętaj, że twoja_nazwa_modelu musi być zapisana małymi literami.

Ola Nguyen Van
źródło
4

Nie jestem pewien, gdzie to się robi w Django, ale możesz to zrobić.

Mapowanie nazwy klasy na ciąg za pomocą odbicia.

classes = [Person,Child,Parent]
def find_class(name):
 for clls in classes:
  if clls.__class__.__name__ == name:
   return clls
jbcurtin
źródło
1
Czy to clls .__ class __.__ name__ czy po prostu clls .__ name__?
Vinayak Kaniyarakkal
4

Kolejna wersja z mniejszą ilością kodu dla leniwych. Przetestowane w Django 2+

from django.apps import apps
model = apps.get_model("appname.ModelName") # e.g "accounts.User"
Gliceryna
źródło
1

Oto podejście mniej specyficzne dla django, aby pobrać klasę z łańcucha:

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

lub możesz użyć importlib, jak pokazano tutaj :

import importlib
myapp_models = importlib.import_module('<appname>.models')
model_a = getattr(myapp_models, 'ModelA')
Schubisu
źródło