Django: 如何从字符串获取模型?

182
在Django中,您可以像这样指定关系:
author = ForeignKey('Person')

然后在内部,它必须将字符串“Person”转换为模型Person

执行此操作的函数在哪里? 我想使用它,但我找不到它。

10个回答

198

16
这个解决方案在 Django 1.7 中已被废弃,请查看这个答案获取新的解决方案:https://dev59.com/VW445IYBdhLWcg3wZJew#26126935 - Tim Saylor
4
嗨@mpen,我建议你更新你的答案。在django 1.9中,get_model定义在AppConfig上(https://docs.djangoproject.com/es/1.9/ref/applications/#django.apps.AppConfig.get_model):`from django.apps import AppConfig` - dani herrera
3
在Django 1.7+中,最好使用Django应用程序注册表上的get_model(),该注册表可通过django.apps.apps.get_model(model_name)获得。AppConfig对象用于不同的目的,并且需要创建一个AppConfig实例才能调用get_model() - zlovelady
3
Django 1.11需要@luke_aus提供的答案。 - Georg Zimmer
7
最新(2020年)的解决方案是:from django.apps import apps 然后: apps.get_model('app_name', 'Model') - Williams
显示剩余5条评论

174

django.db.models.loading 已于 Django1.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'>

我曾经使用过pydoc的“locate”函数...不确定这里的区别,但为了保持在Django中,我已经切换到了这种方法。谢谢。 - warath-coder
4
可选表单 apps.get_model("应用名称.模型名称") - Ricardo D. Quiroga

88

针对任何遇到困难的人(就像我一样):

from django.apps import apps

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

app_name 应该使用引号列出,model_name 也应该使用引号(即不要尝试导入它)

get_model 可以接受小写或大写的 'model_name'


34

大多数模型的“字符串”以“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”格式的话,上面的两个简短的语句就可以工作了。

3
我认为第二行应该是:your_model = get_model(*your_string.rsplit('.', 1))。应用标签有时会使用点分格式,但模型名称始终是有效的标识符。 - Rockallite
1
在新的apps.get_model中似乎不需要这个了。"作为一种快捷方式,此方法还接受一个以app_label.model_name形式表示的单个参数。" - Ryne Everett

23

19

在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

15

如果您不知道模型位于哪个应用程序中,可以通过以下方式搜索:

如果您不知道模型位於哪个应用程式中,可以透过下列方式搜寻:

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

记住,your_model_name 必须为小写。


9

懒人专用的更少代码版本。在Django 2+中测试通过。

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

1
很好的回答,顺便说一下,字符串不需要大写,appname.modelname也可以使用。 - undefined

4
我不确定在Django中是如何完成的,但您可以使用以下方法:
通过反射将类名映射为字符串。
classes = [Person,Child,Parent]
def find_class(name):
 for clls in classes:
  if clls.__class__.__name__ == name:
   return clls

1
是 clls.class.name 还是仅仅 clls.__name__? - Vinayak Kaniyarakkal

2

这里是一种不那么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')

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接