Django 的 settings 文件为什么不能小写?部分源码分析

53 篇文章 0 订阅

背景

我们只知道在配置文件中定义的常量要大写,但没测试过为什么要大写,如果小写的话会有效果吗?会报错吗?接下来一探究竟。

测试

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)

总结

  1. 配置文件大写的原因也只是因为源码只处理全大写的属性而已
  2. Django启动时先加载自己的配置文件, 然后再加载用户的配置文件覆盖掉默认的属性,
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值