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 中獲取。

相關文章
相關標籤/搜索