在以前幾篇的描述中,在 django
的配置文件 settings
也能看到一些配置的引用。例如 INSTALLED_APPS
、MIDDLEWARE
、ROOT_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
中獲取。