Django: получить модель из строки?

133

В Django вы можете указать такие отношения, как:

author = ForeignKey('Person')

А затем внутренне он должен преобразовать строку «Человек» в модель Person.

Где функция, которая это делает? Я хочу его использовать, но не могу найти.

mpen
источник

Ответы:

167

Начиная с Django 3.0, это AppConfig.get_model(model_name, require_ready=True)

Начиная с Django 1.9 метод django.apps.AppConfig.get_model(model_name).
- данихп

Начиная с Django 1.7 django.db.models.loadingон устарел (будет удален в 1.9) в пользу новой системы загрузки приложений.
- Скотт Вудалл


Нашел это. Здесь это определяется:

from django.db.models.loading import get_model

Определяется как:

def get_model(self, app_label, model_name, seed_cache=True):
mpen
источник
11
Это решение устарело в Django 1.7, см. Другой ответ для решения: stackoverflow.com/a/26126935/155987
Тим Сэйлор
4
Привет @mpen, предлагаю обновить свой ответ. В django 1.9 get_model определяется в AppConfig :from django.apps import AppConfig
dani herrera,
1
django.apps.AppConfig - это тип генератора, который не может работать как объект модели.
Neeraj Kumar
2
В Django 1.7+ лучше использовать get_model () в реестре приложений Django, который доступен через django.apps.apps.get_model(model_name). Объекты AppConfig предназначены для другой цели и требуют от вас создания экземпляра AppConfig для вызова get_model().
zlovelady
2
Django 1.11 нужен ответ от @luke_aus
Георг Циммер
132

django.db.models.loadingбыл устаревший в Django 1.7 ( удалено в версии 1.9 ) в пользу новой системы загрузки приложений .

Документы Django 1.7 вместо этого дают нам следующее:

>>> from django.apps import apps
>>> User = apps.get_model(app_label='auth', model_name='User')
>>> print(User)
<class 'django.contrib.auth.models.User'>
Скотт Вудалл
источник
Раньше я использовал функцию "locate" pydoc ... не уверен, в чем разница, но чтобы оставаться в Django, я переключился на этот метод. Спасибо.
warath-coder
61

просто для тех, кто застрял (как и я):

from django.apps import apps

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

app_nameдолжны быть указаны в кавычках, как и должно model_name(т.е. не пытайтесь импортировать его)

get_model принимает "имя_модели" в нижнем или верхнем регистре

lukeaus
источник
32

Большинство "строк" модели имеют вид "appname.modelname", поэтому вы можете использовать этот вариант для get_model.

from django.db.models.loading import get_model

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

Часть кода django, которая обычно превращает такие строки в модель, немного сложнее. Это из 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)

На мой взгляд, это хороший случай для разделения этого на одну функцию в основном коде. Однако, если вы знаете, что ваши строки находятся в формате «App.Model», два приведенных выше лайнера будут работать.

Ch'marr
источник
3
Я думаю , что вторая линия должна быть: your_model = get_model(*your_string.rsplit('.', 1)). Ярлык приложения иногда имеет формат с точками, однако название модели всегда является допустимым идентификатором.
Rockallite
1
Похоже, в новом в этом нет необходимости apps.get_model. «В качестве ярлыка этот метод также принимает один аргумент в форме app_label.model_name».
Райн Эверетт
16

Благословенный способ сделать это в Django 1.7+:

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

Итак, в каноническом примере всех руководств по фреймворку:

import django
entry_cls = django.apps.apps.get_model('blog', 'entry')  # Case insensitive
Крейг Лабенц
источник
9

Если вы не знаете, в каком приложении существует ваша модель, вы можете найти ее следующим образом:

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

Помните, что your_model_name должно быть в нижнем регистре.

Ола Нгуен Ван
источник
4

Я не уверен, где это делается в Django, но вы могли бы это сделать.

Сопоставление имени класса со строкой через отражение.

classes = [Person,Child,Parent]
def find_class(name):
 for clls in classes:
  if clls.__class__.__name__ == name:
   return clls
jbcurtin
источник
1
Это clls .__ class __.__ name__ или просто clls .__ name__?
Vinayak Kaniyarakkal
4

Еще одно исполнение с меньшим количеством кода для ленивых. Протестировано в Django 2+

from django.apps import apps
model = apps.get_model("appname.ModelName") # e.g "accounts.User"
Глицерин
источник
1

Вот менее специфичный для django подход для получения класса из строки:

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

или вы можете использовать importlib, как показано здесь :

import importlib
myapp_models = importlib.import_module('<appname>.models')
model_a = getattr(myapp_models, 'ModelA')
Schubisu
источник