上次我们了解了django启动原理,细心的朋友可能发现django中的setting配置文件加载时懒加载,接下来我们了解下setting的懒加载
我们从manager.py进入management/__init__.py,我们可以看到导入有这个from django.conf import settings,我们进入setting后就可以看到懒加载源码(为了方便理解拿掉部分代码)
class LazySettings(LazyObject):
"""
全局Django设置或自定义设置对象的惰性代理。
用户可以在使用之前手动配置设置。 除此以外,Django使用DJANGO_SETTINGS_MODULE指向的设置模块。
"""
def _setup(self, name=None):
"""
加载环境变量指向的设置模块。 这个如果用户尚未使用,则在第一次需要设置时使用手动配置的设置。
"""
# 这个是获取我们manager.py里的os.environ.setdefault("DJANGO_SETTINGS_MODULE", "project.settings")
settings_module = os.environ.get(ENVIRONMENT_VARIABLE)
if not settings_module:
# 如果没有则抛出异常
pass
self._wrapped = Settings(settings_module)
def __getattr__(self, name):
# 返回设置的值并将其缓存在self .__ 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):
"""
设置设置值。 如果_wrapped发生更改,则清除所有缓存的值(@override_settings执行此操作),或者在设置时清除单个值
"""
if name == '_wrapped':
self.__dict__.clear()
else:
self.__dict__.pop(name, None)
super().__setattr__(name, value)
def __delattr__(self, name):
"""
删除设置并根据需要从缓存中清除它。
"""
super().__delattr__(name)
self.__dict__.pop(name, None)
# 其他方法
settings = LazySettings()
懒加载:在需要用到的时候再加载。我们一般用代理类,线程。在django 中使用 LazyObject
代理类。加载函数是 _setup
函数,当获取属性时才会去加载。
LazySettings
重写了 __getattr__
, __setattr__
,__delattr__和_setup方法,那么在调用 settings.INSTALLED_APPS
时,会通过其自定义的 __getattr__
方法实现,从中可以看出,所有属性都是从 self._wrapped
也就是 Settings(settings_module)
这个实例中取得的。
首先通过os.environ.get(ENVIRONMENT_VARIABLE)获取当前环境变量中的DJANGO_SETTINGS_MODULE,DJANGO_SETTINGS_MODULE 是我们在manager.py中设置的(os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'project.settings'))。通过getter和setter,对 settings对象的操作转到其私有成员 self._wrapped
对象的调用上,这里在第一次使用settings对象时,将其私有成员 self._wrapped
初始化为 Settings
类实例:
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
# # 加载配置文件 settings.py
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)
if (setting in tuple_settings and
not isinstance(setting_value, (list, tuple))):
# 抛出错误
pass
setattr(self, setting, setting_value)
self._explicit_settings.add(setting)
if not self.SECRET_KEY:
# 如果没有SECRET_KEY抛出错误
pass
if hasattr(time, 'tzset') and self.TIME_ZONE:
# 验证时区。
zoneinfo_root = Path('/usr/share/zoneinfo')
zone_info_file = zoneinfo_root.joinpath(*self.TIME_ZONE.split('/'))
if zoneinfo_root.exists() and not zone_info_file.exists():
raise ValueError("Incorrect timezone setting: %s" % self.TIME_ZONE)
# 将时区信息移到os.environ中
os.environ['TZ'] = self.TIME_ZONE
time.tzset()
首先通过 global_settings 来设置其属性,接着读取 project.settings设置其属性,主要有 INSTALLED_APPS,TEMPLATE_DIRS,LOCALE_PATHS这几个key,默认都是空元组。源码中使用 importlib.import_module 来加载用户自定义的配置模块,完成全局实例的初始化。项目实例化后,配置懒加载也就完成了,程序就回到 execute 函数,接下去就是运行 django.setup() 函数了。
execute函数的部分代码
def execute(self):
try:
subcommand = self.argv[1]
except IndexError:
subcommand = 'help' # Display help if no arguments were given.
# 预处理选项以提取--settings和--pythonpath
parser = CommandParser(usage='%(prog)s subcommand [options] [args]', add_help=False, allow_abbrev=False)
parser.add_argument('--settings')
parser.add_argument('--pythonpath')
parser.add_argument('args', nargs='*') # catch-all
try:
options, args = parser.parse_known_args(self.argv[2:])
handle_default_options(options)
except CommandError:
pass
try:
settings.INSTALLED_APPS
except ImproperlyConfigured as exc:
self.settings_exception = exc
except ImportError as exc:
self.settings_exception = exc
if settings.configured:
if subcommand == 'runserver' and '--noreload' not in self.argv:
try:
# 对错误进行捕捉
autoreload.check_errors(django.setup)()
except Exception:
pass
else:
django.setup()
"INSTALLED_APPS","TEMPLATE_DIRS","LOCALE_PATHS"的解释如下:
class LazyProxy:
def __init__(self, cls, *args, **kwargs):
self.__dict__['_cls'] = cls
self.__dict__['_params'] = args
self.__dict__['_kwargs'] = kwargs
self.__dict__['_obj'] = None
def __getattr__(self, item):
if self.__dict__['_obj'] is None:
self._init_obj()
return getattr(self.__dict__['_obj'], item)
def __setattr__(self, key, value):
if self.__dict__['_obj'] is None:
self._init_obj()
setattr(self.__dict__['_obj'], key, value)
def _init_obj(self):
self.__dict__['_obj'] = object.__new__(self.__dict__['_cls'])
self.__dict__['_obj'].__init__(*self.__dict__['_params'], **self.__dict__['_kwargs'])
class LazyInit:
def __new__(cls, *args, **kwargs):
return LazyProxy(cls, *args, **kwargs)
class A(LazyInit):
def __init__(self, x):
print("Init A")
self.x = x
Python之实例懒加载实现
python中的懒加载模块