Django采用懒加载的方式将配置文件的加载到全局变量对象settings = LazySettings()的属性中
举例说明
-
程序入口
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'mysite.settings') # 将配置文件模块的相对路径加载到环境变量中
-
#!/usr/bin/env python
"""Django's command-line utility for administrative tasks."""
import os
import sys
def main():
"""Run administrative tasks."""
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'mysite.settings') # 将配置文件模块的相对路径加载到环境变量中
try:
from django.core.management import execute_from_command_line
except ImportError as exc:
raise ImportError(
"Couldn't import Django. Are you sure it's installed and "
"available on your PYTHONPATH environment variable? Did you "
"forget to activate a virtual environment?"
) from exc
execute_from_command_line(sys.argv)
if __name__ == '__main__':
main()
- 跳转到下面,重点看ManagementUtility 的execute方法的 settings.INSTALLED_APPS
首先settings=LzaySettings()对象,访问属性INSTALLED_APPS时,会经过 getattr(self, name)方法,
一. 第一次启动时self._wrapped is empty(empty是LazyObject的类属性)
self._wrapped = Settings(settings_module)
Settings中先将django.conf.global_settings.py中默认大写全局变量配置到settings的属性中.
mod = importlib.import_module(self.SETTINGS_MODULE) #导入os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'mysite.settings')模块,dir才能解析模块
for setting in dir(mod): #用dir来获取所有变量 if setting.isupper(): value = getattr(mod, setting) if (setting in tuple_settings and not isinstance(value, (list, tuple))): raise ImproperlyConfigured("The %s seting must be a list or tuple" % setting) setattr(self, setting, value) # 设置self的属性
二. 后面再访问属性时直接从self._wrapped.dict(settings.wrapped.dict)中获取
以下是涉及到的简化源码
def execute_from_command_line(argv=None):
"""Run a ManagementUtility."""
utility = ManagementUtility(argv)
utility.execute()
def execute(self):
...
try:
settings.INSTALLED_APPS
print("se:",settings.INSTALLED_APPS)
except ImproperlyConfigured as exc:
self.settings_exception = exc
except ImportError as exc:
self.settings_exception = exc
settings = LazySettings() # 全局对象
class LazySettings(LazyObject):
def _setup(self, name=None):
settings_module = os.environ.get(ENVIRONMENT_VARIABLE)
if not settings_module:
desc = ("setting %s" % name) if name else "settings"
raise ImproperlyConfigured(
"Requested %s, but settings are not configured. "
"You must either define the environment variable %s "
"or call settings.configure() before accessing settings."
% (desc, ENVIRONMENT_VARIABLE))
self._wrapped = Settings(settings_module)
def __getattr__(self, name):
"""Return the value of a setting and cache it in self.__dict__."""
if self._wrapped is empty:
self._setup(name)
val = getattr(self._wrapped, name)
self.__dict__[name] = val
return val
class Settings:
def __init__(self, settings_module):
for setting in dir(global_settings):
if setting.isupper():
setattr(self, setting, getattr(global_settings, setting)) # 默认的全局变量(大写)
# store settings
self.SETTINGS_MODULE = settings_module
mod = importlib.import_module(self.SETTINGS_MODULE) # 导入os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'mysite.settings')模块,dir才能解析模块
tuple_settings = (
"INSTALLED_APPS",
"TEMPLATE_DIRS",
"LOCALE_PATHS",
)
self._explicit_settings = set()
for setting in dir(mod): #用dir来获取所有变量
if setting.isupper():
value = getattr(mod, setting)
if (setting in tuple_settings and not isinstance(value, (list, tuple))):
raise ImproperlyConfigured("The %s seting must be a list or tuple" % setting)
setattr(self, setting, value) # 设置self的属性
empty = object()
class LazyObject:
# Avoid infinite recursion when tracing __init__ (#19456).
_wrapped = None
def __init__(self):
# Note: if a subclass overrides __init__(), it will likely need to
# override __copy__() and __deepcopy__() as well.
self._wrapped = empty
__getattr__ = new_method_proxy(getattr)
def __setattr__(self, name, value):
if name == "_wrapped":
# Assign to __dict__ to avoid infinite __setattr__ loops.
self.__dict__["_wrapped"] = value
else:
if self._wrapped is empty:
self._setup()
setattr(self._wrapped, name, value)
def _setup(self):
"""
Must be implemented by subclasses to initialize the wrapped object.
"""
raise NotImplementedError('subclasses of LazyObject must provide a _setup() method')