本文是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 中,是不能读取表模型的,因为这个阶段,还未加载表模型
本次复盘结束.