django源码分析--03app加载过程
2017-01-22 本文已影响554人
极光火狐狸
django.core.management.__init__.ManagementUtility.execute
方法中通过autoreload.check_errors(django.setup)()
这一行代码来加载django的app。
django.__init__.setup
方法中通过django.apps.registry.Apps.populate(settings.INSTALLED_APPS)
来加载所有配置文件中定义好的app。刚创建好的一个project默认模版都会在settings.py文件中指定好这几个app:
django.contrib.admin
django.contrib.auth
django.contrib.contenttypes
django.contrib.sessions
django.contrib.messages
django.contrib.staticfiles
django.apps.registry.Apps.populate
方法中通过下面这个代码片段调用django.apps.config.AppConfig.create
的类方法来加载app和实例化该app的类对象。
django.apps.registry.py
from .config import AppConfig
class Apps(object):
def __init__(self, installed_apps=()):
if installed_apps is None and hasattr(sys.modules[__name__], 'apps'):
raise RuntimeError("You must supply an installed_apps argument.")
self.all_models = defaultdict(OrderedDict)
self.app_configs = OrderedDict()
self.stored_app_configs = []
self.apps_ready = self.models_ready = self.ready = False
self._lock = threading.Lock()
self._pending_operations = defaultdict(list)
if installed_apps is not None:
self.populate(installed_apps)
def populate(self, installed_apps=None):
if self.ready:
return
with self._lock:
if self.ready:
return
if self.app_configs:
raise RuntimeError("populate() isn't reentrant")
for entry in installed_apps:
if isinstance(entry, AppConfig):
app_config = entry
else:
# 这里这个for循环落实的工作是,
# 实例化每个app的配置管理对象(
# django.contrib.auth.apps.AuthConfig()),
# 实例化过后默认有很多参数都是None,
# 例如models_module、models等。
app_config = AppConfig.create(entry)
if app_config.label in self.app_configs:
raise ImproperlyConfigured(
"Application labels aren't unique, "
"duplicates: %s" % app_config.label)
self.app_configs[app_config.label] = app_config
counts = Counter(
app_config.name for app_config in self.app_configs.values())
duplicates = [
name for name, count in counts.most_common() if count > 1]
if duplicates:
raise ImproperlyConfigured(
"Application names aren't unique, "
"duplicates: %s" % ", ".join(duplicates))
self.apps_ready = True
# Load models.
for app_config in self.app_configs.values():
# 这里采用字典引用的方式赋值给all_models,
# 也就是说后续self.all_models['django.contrib.auth']
# 发生变化,它也会发生变化。
all_models = self.all_models[app_config.label]
# 这里采用了非常高级的做法,以我现在的水平,
# 暂时还不能完全理解,但是我已经有一些眉目了,
# 详细的说明请进入import_models方法中继续查看。
app_config.import_models(all_models)
self.clear_cache()
self.models_ready = True
for app_config in self.get_app_configs():
# 这里是针对app进行检查是否已经存在,
# 如果已经存在则不做事情,如果不存在则
# 将它添加到CheckRegistry.registered_checks和
# 将它添加到CheckRegistry.deployment_checks的
# 注册列表中。
# 备注: 维护这个列表的用意我暂时还不清楚。
app_config.ready()
self.ready = True
django.apps.config.py
class AppConfig(object):
def __init__(self, app_name, app_module):
self.name = app_name
self.module = app_module
@classmethod
def create(cls, entry):
try:
# entry = 'django.contrib.auth' , module = <module 'django.contrib.auth' from 'C:\\Python35\\lib\\site-packages\\django\\contrib\\auth\\__init__.py'>
module = import_module(entry)
except ImportError:
module = None
mod_path, _, cls_name = entry.rpartition('.')
if not mod_path:
raise
else:
try:
# entry = 'django.contrib.auth.apps.AuthConfig'
entry = module.default_app_config
except AttributeError:
return cls(entry, module)
else:
# mod_path = 'django.contrib.auth.apps',
# _ = '.' ,
# cls_name = 'AuthConfig'
mod_path, _, cls_name = entry.rpartition('.')
# mod = <module 'django.contrib.auth.apps' from 'C:\\Python35\\lib\\site-packages\\django\\contrib\\auth\\apps.py'>
mod = import_module(mod_path)
try:
# cls = <class django.contrib.auth.apps.AuthConfig>
cls = getattr(mod, cls_name)
except AttributeError:
if module is None:
import_module(entry)
else:
raise
if not issubclass(cls, AppConfig):
raise ImproperlyConfigured(
"'%s' isn't a subclass of AppConfig." % entry)
try:
# app_name = cls.name = 'django.contrib.auth'
app_name = cls.name
except AttributeError:
raise ImproperlyConfigured(
"'%s' must supply a name attribute." % entry)
# app_module = <module 'django.contrib.auth' from 'C:\\Python35\\lib\\site-packages\\django\\contrib\\auth\\__init__.py'>
app_module = import_module(app_name)
# <AuthConfig: auth> = <django.contrib.auth.apps.AuthConfig object app_name='django.contrib.auth' app_module='<module 'django.contrib.auth' from 'C:\\Python35\\lib\\site-packages\\django\\contrib\\auth\\__init__.py'>' at 0x000001E02D9AE470>
return cls(app_name, app_module)
def import_models(self, all_models):
self.models = all_models
if module_has_submodule(self.module, MODELS_MODULE_NAME):
# self.name = app_name = 'django.contrib.auth' , MODELS_MODULE_NAME = 'models' ; models_module_name = 'django.contrib.auth.models'
models_module_name = '%s.%s' % (self.name, MODELS_MODULE_NAME)
# 这里将'django.contrib.auth.models'整个文件模块形式的导入到self.models_module变量中.
# 但是由于'django.contrib.auth.models'的class中有继承ModelBase,然后ModelBase.__new__,
# 在导入过程中会被触发并执行__new__下的代码块,该代码块中对
# apps.get_containing_app_config('django.contrib.auth')单独进行register_model(模型注册),
# 因此它会再次更新self.all_models,然后all_models会跟着发生变化,然后这里的self.models也会跟着发生变化。
# 最终呈现出来的是: <django.contrib.auth.AppConfig object self.models=OrderedDict([('permission', <class 'django.contrib.auth.models.Permission'>), ('group_permissions', <class 'django.contrib.auth.models.Group_permissions'>), ('group', <class 'django.contrib.auth.models.Group'>), ('user_groups', <class 'django.contrib.auth.models.User_groups'>), ('user_user_permissions', <class 'django.contrib.auth.models.User_user_permissions'>), ('user', <class 'django.contrib.auth.models.User'>)]) at 0x000001E02D9AE470>
# 通过呈现结果的对象来看,self.models已经装载了这些<已经加载好的数据库>对象。
self.models_module = import_module(models_module_name)
# self.models_module = import_module(models_module_name) = import_module('django.contrib.auth.models')
总结
django在启动wsgi之前,会调用django.apps模块来读取项目文件中的settings.py拿到这面这几个app,然后交给django.apps的registry.py和config.py来进行统一的配置加载、实例化(含models数据库对象)。