Django源码分析(七):settings懒加载

起步

在以前几篇的描述中,在 django 的配置文件 settings 也能看到一些配置的引用。例如 INSTALLED_APPSMIDDLEWAREROOT_URLCONF 等配置。python

那么这个配置模块是如何实现的呢?django

开始

通常咱们引用 django 配置推荐这样引入缓存

from django.conf import settings
复制代码

而不是app

from proj import settings
复制代码

若是使用第二种形式的话,settings 中引用一些其它模块的话,那么会有可能形成循环引用。使用第一种形式的话,django 使用的是懒加载机制(用到的时候才加载)。函数

详情咱们能够经过源码去查看。spa

了解

settings = LazySettings()

class LazySettings(LazyObject):
    def _setup(self, name=None):
        # 从环境变量中提取 settings 模块路径
        settings_module = os.environ.get(ENVIRONMENT_VARIABLE)
        self._wrapped = Settings(settings_module)

    def __getattr__(self, name):
        """返回设置的值并缓存在 __dict__ 中"""
        if self._wrapped is empty:
            self._setup(name)
        val = getattr(self._wrapped, name)
        self.__dict__[name] = val
        return val

    def __setattr__(self, name, value):
        if name == '_wrapped':
            self.__dict__.clear()
        else:
            self.__dict__.pop(name, None)
        super().__setattr__(name, value)

    ... # 省略其它函数
复制代码

所谓懒加载,就是须要的时候才去加载。django 经过代理类 LazyObject 实现这个机制。加载函数是 _setup,当获取属性时才去加载,并缓存至实例的 __dict__ 中。代理

LazySettings 继承了 LazyObject,重写了 __setattr____getattr__,假设调用 settings.DEBUG 属性时,会调用 __getattr__ 方法实现。code

自此,咱们能够观察到,全部的属性都是从 _wrapped (也就是 Settings(settings_module) 实例)这个私有属性中获取到的。继承

配置加载

上述咱们了解到从环境变量中提取 settings 模块的路径,继而 _wrapped 属性指向 Settings 这个类的实例。ci

class Settings:
    def __init__(self, settings_module):
        # 读取默认配置
        for setting in dir(global_settings):
            if setting.isupper():
                setattr(self, setting, getattr(global_settings, setting))

        # 配置模块
        self.SETTINGS_MODULE = settings_module

        # 动态导入
        mod = importlib.import_module(self.SETTINGS_MODULE)

        tuple_settings = (
            "INSTALLED_APPS",
            "TEMPLATE_DIRS",
            "LOCALE_PATHS",
        )
        self._explicit_settings = set()
        # 读取配置模块下的属性(可能会覆盖一些默认配置)
        for setting in dir(mod):
            if setting.isupper():
                setting_value = getattr(mod, setting)
                setattr(self, setting, setting_value)
                self._explicit_settings.add(setting)
复制代码

总结

综上,咱们在读取 settings 某个配置时,会触发 __getattr__ 方法,若是 _wrapped 为空,则调用 _setup 方法,这个方法内部获取配置文件模块,_wrapped 属性指向 Settings 类的实例,这个类在实例化的时候,构造函数先读取 global_settings 来设置一些默认属性,接着经过动态导入模块的形式 importlib.import_module 加载配置模块的属性,继而读取的属性从 _wrapped 中获取。

相关文章
相关标签/搜索