Django源码阅读(2)INSTALLED_APPS加载过程

  • 再次回到上节刚开始的 execute 函数,接下来我们就回过头来分析一下 django.setup() 函数,看看它里面是如何进行加载配置的 。
# django\core\management\__init__.py
def execute(self):
    ...
    if settings.configured:
        # Start the auto-reloading dev server even if the code is broken.
        # The hardcoded condition is a code smell but we can't rely on a
        # flag on the command class because we haven't located it yet.
        if subcommand == 'runserver' and '--noreload' not in self.argv:
            try:
                autoreload.check_errors(django.setup)()
            except Exception:
                # The exception will be raised later in the child process
                # started by the autoreloader. Pretend it didn't happen by
                # loading an empty list of applications.
                apps.all_models = defaultdict(OrderedDict)
                apps.app_configs = OrderedDict()
                apps.apps_ready = apps.models_ready = apps.ready = True

                # Remove options not compatible with the built-in runserver
                # (e.g. options for the contrib.staticfiles' runserver).
                # Changes here require manually testing as described in
                # #27522.
                _parser = self.fetch_command('runserver').create_parser('django', 'runserver')
                _options, _args = _parser.parse_known_args(self.argv[2:])
                for _arg in _args:
                    self.argv.remove(_arg)

        # In all other cases, django.setup() is required to succeed.
        else:
            django.setup()
    ...

django.setup() 启动程序

  • 这个代码在 django/init.py 中
def setup(set_prefix=True):
    """
    Configure the settings (this happens as a side effect of accessing the
    first setting), configure logging and populate the app registry.
    Set the thread-local urlresolvers script prefix if `set_prefix` is True.
    """
    ...
    configure_logging(settings.LOGGING_CONFIG, settings.LOGGING)
    if set_prefix:
        set_script_prefix(
            '/' if settings.FORCE_SCRIPT_NAME is None else settings.FORCE_SCRIPT_NAME
        )
    apps.populate(settings.INSTALLED_APPS)
  • configure_logging 配置日志信息,set_script_prefix 设置一些前缀补充完整,这两个方法直接过。我们看如下有价值的代码,是调用了模块from django.apps import apps的类方法populate,加载 settings.INSTALLED_APPS 中的自定义app(字符串),以及 对应的 models 模块,并保存在 django.apps 中,这是一个全局的 Apps 类(这个类在 django.apps.registry 中定义,populate(self, installed_apps=None) 是它的主要方法。)实例。是一个已经安装应用的注册表,这个注册表存储着配置信息以及用来自省,同时也维护这模型的列表。

模型的加载

    def populate(self, installed_apps=None):
        # populate() might be called by two threads in parallel on servers
        # that create threads before initializing the WSGI callable.
        with self._lock:
            ...
            for entry in installed_apps:
                if isinstance(entry, AppConfig):
                    app_config = entry
                else:
                    app_config = AppConfig.create(entry)
                ...
                self.app_configs[app_config.label] = app_config
                app_config.apps = self
            ...
  • populate函数通过with开启了self._lock = threading.Lock()锁,所以是线程安全的,然后循环installed_apps,如果是AppConfig实例类型就返回,否则就去加载 app 并实例化该 app 的类对象app_config = AppConfig.create(entry),保存在 self.app_configs 中。
  • create方法是 classmethod 的,这是一个工厂模式,它根据参数来构造出 AppConfig(app_name, app_module) 这样的实例。
        try:
            app_name = cls.name
        except AttributeError:
            raise ImproperlyConfigured(
                "'%s' must supply a name attribute." % entry)

        # Ensure app_name points to a valid module.
        try:
            app_module = import_module(app_name)
  • 其中 app_name 表示 INSTALLED_APPS 中指定的应用字符串,app_module 表示根据 app_name 加载到的module。在 AppConfig 实例的初始化方法中,会记录这些应用的标签、文件路径等信息,最终将这些实例会保存在其属性中。
            # Phase 2: import models modules.
            for app_config in self.app_configs.values():
            # 当加载完毕后,进行导入各个app模块
                app_config.import_models()

            self.clear_cache()

            self.models_ready = True

            # Phase 3: run ready() methods of app configs.
            for app_config in self.get_app_configs():
            # 然后修改其状态
                app_config.ready()

            self.ready = True
            self.ready_event.set()
            
#输出 self.get_app_configs()
odict_values([<AdminConfig: admin>, 
<AuthConfig: auth>, 
<ContentTypesConfig: contenttypes>, 
<SessionsConfig: sessions>, 
<MessagesConfig: messages>,
...
<AppConfig: pure_pagination>])

#输出 import_models 函数中的 self.models_module
<module 'django.contrib.admin.models' from 'C:\\mooc_project\\lib\\site-packages\\django\\contrib\\admin\\models.py'>
<module 'django.contrib.auth.models' from 'C:\\mooc_project\\lib\\site-packages\\django\\contrib\\auth\\models.py'>
<module 'django.contrib.contenttypes.models' from 'C:\\mooc_project\\lib\\site-packages\\django\\contrib\\contenttypes\\models.py'>
<module 'django.contrib.sessions.models' from 'C:\\mooc_project\\lib\\site-packages\\django\\contrib\\sessions\\models.py'>
#呈现结果的对象来看,self.models 已经装载了这些 已经加载好 的模型对象。

你可能感兴趣的:(Django源码阅读)