本文是2023年技术复盘文章之一。复盘带来思考,通过思考取其精华,精进自我,提升赚钱能力。
目录:
1、
2、总结
概念声明:
1、配置文件、项目配置文件,均指项目的配置文件,即项目目录下的
2、内置配置文件,指的是
一、settings.py 是如何被项目加载的
从入口文件开始溯源,
#!/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', 'aiserver.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()
第一步:
首先把
贴士:
os.environ 会读取系统环境变量到当前进程,os.environ.setdefault 在此基础上,增加当前进程的环境变量,两个操作都不会改变系统的环境变量设置。可以看出,django 是使用这个方法来解耦代码和配置的。
第二步:
通过
def execute_from_command_line(argv=None):
"""Run a ManagementUtility."""
# 一灯注:实例化了一个命令行管理工具
utility = ManagementUtility(argv)
# 一灯注:调用该工具实例的execute方法
utility.execute()
抛开和主题无关的,直接看
def execute(self):
# 省略其他代码
try:
# 一灯注:在此方法中,首次使用了settings
settings.INSTALLED_APPS
except ImproperlyConfigured as exc:
self.settings_exception = exc
except ImportError as exc:
self.settings_exception = exc
# 省略其他代码
settings.INSTALLED_APPS
假装思考:
1、并未保存返回值,先假设不清楚它是否有返回值;
2、通过字面得知其获取了
3、
也就是说,
第三步:
直接找到
from django.conf import settings
在开发过程中,经常引入这个模块来读取当前的配置
顺进去继续看看,它是如何把配置文件加载到项目中的。这个模块就保存在
settings = LazySettings()
望文生义,看到
延迟的原因猜测:在使用
python manage.py runserver 时,可使用--settings 来指定配置文件,意味着可以有多个配置文件,而在manage.py 中通过环境变量指定了配置文件的路径,如果不指定自定义的配置文件,则会使用环境变量中默认设置的,故采用延迟加载的方式。
第四步:
LazySettings().INSTALLED_APPS
但
def __getattr__(self, name):
"""Return the value of a setting and cache it in self.__dict__."""
if (_wrapped := self._wrapped) is empty:
self._setup(name)
_wrapped = self._wrapped
val = getattr(_wrapped, name)
# Special case some settings which require further modification.
# This is done here for performance reasons so the modified value is cached.
if name in {"MEDIA_URL", "STATIC_URL"} and val is not None:
val = self._add_script_prefix(val)
elif name == "SECRET_KEY" and not val:
raise ImproperlyConfigured("The SECRET_KEY setting must not be empty.")
self.__dict__[name] = val
return val
逐一分析
1、防止反复加载配置文件的措施
if (_wrapped := self._wrapped) is empty:
self._setup(name)
_wrapped = self._wrapped
只有首次获取配置文件内容时才会加载,也就是前面所提到的
在这个方法中,可以看到终于开始从环境变量中读取
def _setup(self, name=None):
"""
Load the settings module pointed to by the environment variable. This
is used the first time settings are needed, if the user hasn't
configured settings manually.
"""
# 一灯注:ENVIRONMENT_VARIABLE = "DJANGO_SETTINGS_MODULE"
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)
)
# 一灯注:同时,把活委托给了Settings,入参是配置文件的路径关系
self._wrapped = Settings(settings_module)
还没完,真正去加载配置的并非
3、
最后一步:
注:除了项目配置文件,
django 还会有内置的配置文件,声明所有的配置项目的默认值
class Settings:
def __init__(self, settings_module):
# update this dict from global settings (but only for ALL_CAPS settings)
# 一灯注:加载了内置的配置文件项目
for setting in dir(global_settings):
# 一灯注:只加载大写的配置项目
if setting.isupper():
# 一灯注:将内置的配置项目设置为类实例的属性
setattr(self, setting, getattr(global_settings, setting))
# store the settings module in case someone later cares
# 一灯注:设置SETTINGS_MODULE为项目配置文件路径关系 其他地方用得着,配置项目很多,有些项目在其他流程要用的,意味着Settings类实力在存在很长的生命周期,比如中间件需要用
self.SETTINGS_MODULE = settings_module
# 一灯注:动态导入配置文件
mod = importlib.import_module(self.SETTINGS_MODULE)
tuple_settings = (
"ALLOWED_HOSTS",
"INSTALLED_APPS",
"TEMPLATE_DIRS",
"LOCALE_PATHS",
"SECRET_KEY_FALLBACKS",
)
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)
):
raise ImproperlyConfigured(
"The %s setting must be a list or a tuple." % setting
)
setattr(self, setting, setting_value)
self._explicit_settings.add(setting)
if self.USE_TZ is False and not self.is_overridden("USE_TZ"):
warnings.warn(
"The default value of USE_TZ will change from False to True "
"in Django 5.0. Set USE_TZ to False in your project settings "
"if you want to keep the current default behavior.",
category=RemovedInDjango50Warning,
)
if self.is_overridden("USE_DEPRECATED_PYTZ"):
warnings.warn(USE_DEPRECATED_PYTZ_DEPRECATED_MSG, RemovedInDjango50Warning)
if self.is_overridden("CSRF_COOKIE_MASKED"):
warnings.warn(CSRF_COOKIE_MASKED_DEPRECATED_MSG, RemovedInDjango50Warning)
if hasattr(time, "tzset") and self.TIME_ZONE:
# When we can, attempt to validate the timezone. If we can't find
# this file, no check happens and it's harmless.
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)
# Move the time zone info into os.environ. See ticket #2315 for why
# we don't do this unconditionally (breaks Windows).
os.environ["TZ"] = self.TIME_ZONE
time.tzset()
if self.is_overridden("USE_L10N"):
warnings.warn(USE_L10N_DEPRECATED_MSG, RemovedInDjango50Warning)
if self.is_overridden("DEFAULT_FILE_STORAGE"):
if self.is_overridden("STORAGES"):
raise ImproperlyConfigured(
"DEFAULT_FILE_STORAGE/STORAGES are mutually exclusive."
)
warnings.warn(DEFAULT_FILE_STORAGE_DEPRECATED_MSG, RemovedInDjango51Warning)
if self.is_overridden("STATICFILES_STORAGE"):
if self.is_overridden("STORAGES"):
raise ImproperlyConfigured(
"STATICFILES_STORAGE/STORAGES are mutually exclusive."
)
warnings.warn(STATICFILES_STORAGE_DEPRECATED_MSG, RemovedInDjango51Warning)
def is_overridden(self, setting):
return setting in self._explicit_settings
def __repr__(self):
return '<%(cls)s "%(settings_module)s">' % {
"cls": self.__class__.__name__,
"settings_module": self.SETTINGS_MODULE,
}
至此,项目的配置文件,算是全部加载进来了
二、总结
1、
2、项目配置文件,代理类为
3、
4、
5、
6、
- 中间件
- 用户校验
- 数据库配置
- 静态文件
- 省略其他
7、再回过头看
延申:从过分析得知,在
settings.py 中,是不能读取表模型的,因为这个阶段,还未加载表模型
本次复盘结束.