背景
我们只知道在配置文件中定义的常量要大写,但没测试过为什么要大写,如果小写的话会有效果吗?会报错吗?接下来一探究竟。
测试
settings.py
a = 1 # 小写
view.py
class Resource(APIView):
def get(self, request, *args, **kwargs):
print(settings.a)
return Response(ser.data)
发现报错了
Internal Server Error: /api/v1/resource/
Traceback (most recent call last):
File "D:\env\test_model\lib\site-packages\django\core\handlers\exception.py", line 34, in inner
response = get_response(request)
File "D:\env\test_model\lib\site-packages\django\core\handlers\base.py", line 115, in _get_response
response = self.process_exception_by_middleware(e, request)
File "D:\env\test_model\lib\site-packages\django\core\handlers\base.py", line 113, in _get_response
response = wrapped_callback(request, *callback_args, **callback_kwargs)
File "D:\env\test_model\lib\site-packages\django\views\decorators\csrf.py", line 54, in wrapped_view
return view_func(*args, **kwargs)
File "D:\env\test_model\lib\site-packages\django\views\generic\base.py", line 71, in view
return self.dispatch(request, *args, **kwargs)
File "D:\env\test_model\lib\site-packages\rest_framework\views.py", line 505, in dispatch
response = self.handle_exception(exc)
File "D:\env\test_model\lib\site-packages\rest_framework\views.py", line 465, in handle_exception
self.raise_uncaught_exception(exc)
File "D:\env\test_model\lib\site-packages\rest_framework\views.py", line 476, in raise_uncaught_exception
raise exc
File "D:\env\test_model\lib\site-packages\rest_framework\views.py", line 502, in dispatch
response = handler(request, *args, **kwargs)
File "D:\test2\djcelerytest\views.py", line 296, in get
print(settings.a)
File "D:\env\test_model\lib\site-packages\django\conf\__init__.py", line 80, in __getattr__
val = getattr(self._wrapped, name)
AttributeError: 'Settings' object has no attribute 'a'
最主要是这句异常:
AttributeError: 'Settings' object has no attribute 'a'
意思是在 Settings 中没有 a 这个属性,但是上述明明定义了,点进去,跟踪一下,发现是用反射去 self._wrapped 中获取一个 name,猜测可能是获取配置文件中的变量名。
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
上述是从 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.
"""
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_module 是加载我们的配置文件,DJANGO_SETTINGS_MODULE=xx项目.settings
self._wrapped = Settings(settings_module)
Settings 类
先执行 init 方法:
def __init__(self, settings_module):
# update this dict from global settings (but only for ALL_CAPS settings)
for setting in dir(global_settings):
# 这里就是我们要找的地方,是否是大写,如果不是大写,
# 就不往对象中设置值 getattr(self._wrapped) 就会抛出异常
if setting.isupper():
setattr(self, setting, getattr(global_settings, setting))
# store the settings module in case someone later cares
self.SETTINGS_MODULE = settings_module
# 动态导入配置文件
mod = importlib.import_module(self.SETTINGS_MODULE)
# 定义的这三项必须符合规范,列表 or 元祖,不然抛出异常
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))):
raise ImproperlyConfigured("The %s setting must be a list or a tuple. " % setting)
# 将我们配置的属性添加到settings配置文件中, 或者覆盖掉
setattr(self, setting, setting_value)
self._explicit_settings.add(setting)
总结
- 配置文件大写的原因也只是因为源码只处理全大写的属性而已
- Django启动时先加载自己的配置文件, 然后再加载用户的配置文件覆盖掉默认的属性,