自定义 Django models import 方式
默认的,我们会把 Django models 放到一个使用 startapp 创建的 app 的目录下,然后在 settings 中的 INSTALLED_APPS 写入这个 app,在运行 Django 或者做 migrate 时, Django 就会自动发现该 app 下的 models,并在 showmigrations 中显示。
但是如果我们需要将原来分散在各个 Apps 中的所有 models 放到同一个 app 的子目录下,例如原来 models 结构可能是 project/account_app/models.py,我们想改成 project/appcore/account/models.py 。这时候 Django 就会发现不了这些 models 了(无法 makemigrations)。如何才能让 Django 发现这些这些 models 呢?
先来看看 Django 源码中 app 中的属性 (django/apps/config.py):

可以看出 all_models 应该就是该 app 中的所有 models,此外我们还可以发现该类中有个 import_models() 方法,可以看出 models_module_name 为 app_name.models,即每个 app 下的 models 文件,已经写死在这儿了。

查看引用了 import_models 的文件(registry.py)

传入的 all_models 即 models 中的每个类,然后传给 config.py 初始化 models_module。
当 model 被 import 的时候, ModelBase.new 都会调用 apps.register_model 通过参数 all_models 来创建这个 app 对应的 models。
所以想要在子目录中引入 models,我们需要改变 models_module 写死的赋值方式。
比如我们有个 app 叫 appcore,存放所有的 models,目录结构如下:
.
├── README.md
├── myproject
│ ├── __init__.py
│ ├── admin.py
│ ├── settings.py
│ ├── urls.py
│ ├── views
│ └── wsgi.py
├── appcore
│ ├── __init__.py
│ ├── account
│ │ ├── __init__.py
│ │ ├── api.py
│ │ └── models.py
│ ├── apps.py
│ ├── base
│ │ ├── __init__.py
│ │ ├── api.py
│ │ └── models.py
│ ├── comment
│ │ ├── __init__.py
│ │ └── models.py
可以在 apps.py 中覆盖 import_models 方法
# apps.py
from __future__ import unicode_literals
import django
from django.apps import AppConfig
from importlib import import_module
from distutils.version import StrictVersion
# sub module
SUBAPPS = [
'account',
'base',
'commen'
]
class AppCoreConfig(AppConfig):
name = 'appcore'
# django 1.11 后 all_models 获取方式有变化,因此加个判断
if StrictVersion(django.get_version()) >= StrictVersion('1.11'):
def import_models(self):
self.models = self.apps.all_models[self.label]
self._import_models()
else:
# compatibility for django < 1.11
def import_models(self, all_models):
self.models = all_models
self._import_models()
# 添加子目录中的 models
def _import_models(self):
models_module = None
for i in SUBAPPS:
models_module_name = '{}.{}.models'.format(self.name, i)
models_module = import_module(models_module_name)
# 最后重新赋值
self.models_module = models_module
然后在 settings 中添加此 app(appcore), 这样就能被 Django 发现该目录下的所有 models 了。