Django的settings文件部分源码分析

在编写Django项目的过程中, 其中一个非常强大的功能就是我们可以在settings文件配置许多选项来完成我们预期的功能, 并且这些配置还必须大写, 否则就不会生效. 此外, Django自身还有一套更详细的配置, 那Django是如何做到用户配置了相关配置就使用用户的配置, 否则就使用自己默认的配置. 带着这样的疑问, 去查看了用户配置项相关的源码部分.

过程分析

首先启动Django项目, 一般Django都是通过python manage.py runserver这句命令启动的. 从这个入口函数出发, 主要执行了下面3句话.

if __name__ == "__main__":
# settings文件配置到环境变量
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "auth_learn.settings")
from django.core.management import execute_from_command_line
# 解析并执行命令行参数
execute_from_command_line(sys.argv)

上面会将我们用户的配置文件(项目下的settings文件)设置到当前环境变量里面.

顺着代码的流程下去.

def execute_from_command_line(argv=None):
"""
运行了一个命令管理工具, 将命令行的参数传到这个对象中, 并执行
"""
utility = ManagementUtility(argv)
utility.execute()

继续往下运行

def execute(self):
# 解析命令行参数列表的第一个参数
subcommand = self.argv[1]
parser = CommandParser(None, usage="%(prog)s subcommand [options] [args]", add_help=False)
options, args = parser.parse_known_args(self.argv[2:])
handle_default_options(options)
... try:
# 这句话就是重点了, 开始加载app
settings.INSTALLED_APPS
except ImproperlyConfigured as exc:
self.settings_exception = exc ...

看到这, 终于看到了和settings文件相关的代码了. 跟进去.

settings = LazySettings()

看到了settings是一个懒加载(延时加载)的LazySettings类的实例对象.

继续跟进LazySettings的定义

class LazySettings(LazyObject):
def _setup(self, name=None):
settings_module = os.environ.get(ENVIRONMENT_VARIABLE)
self._wrapped = Settings(settings_module)
def __getattr__(self, name):
if self._wrapped is empty:
self._setup(name)
val = getattr(self._wrapped, name)
self.__dict__[name] = val # 用到了直接缓存到__dict__里面
return val
...

发现LazySettings类继承自LazyObject, 本身并没有__init__方法, 所以继续往父类跟进

 _wrapped = None
def __init__(self):
self._wrapped = empty

父类什么也没有定义, 就是一个空对象. 所以settings对象初始化后什么属性也没有, 这时候Django调用settings.INSTALLED_APPS这句话就是懒加载的核心. 所谓懒加载,就是在需要用到的时候再加载. 一般手段有代理类,线程... Django中使用 LazyObject 代理类。加载函数是 _setup 函数,当获取属性时才会去加载。

LazySettings 继承自 LazyObject 类,它重写了 __getattr____setattr__ 方法,那么在调用 settings.INSTALLED_APPS 时,就会触发 __getattr__ 这个双下方法. 我们知道, 初始化的时候, settings对象就是一个empty空对象.这就会去调用加载函数_setup函数

ENVIRONMENT_VARIABLE = "DJANGO_SETTINGS_MODULE"
def _setup(self, name=None):
settings_module = os.environ.get(ENVIRONMENT_VARIABLE)
self._wrapped = Settings(settings_module)

第一句话, setting_module就是从环境变量中获取我们用户自定义的配置文件, 这在刚启动manage.py文件就已经定义好了. 接下来就去实例化一个Settings对象. 从这就可以得出结论通过settings对象的属性都是从_wrapped这个私有属性获取来的, 或者说是从Settings实例对象中获取来的.

继续跟进Settings类的源码.

# global_settings就是一个django内部的全局配置文件
from django.conf import global_settings
class Settings(object):
def __init__(self, settings_module):
# 这句话就是遍历全局配置, 将所有的属性添加到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
self.SETTINGS_MODULE = settings_module
# 这里就是动态的将我们用户的自定义配置文件模块导入
mod = importlib.import_module(self.SETTINGS_MODULE)
self._explicit_settings = set()
# 遍历用户自定义配置文件
for setting in dir(mod):
# 如果我们配置的属性不是大写, 就会无效
if setting.isupper():
# 获取用户的配置属性
setting_value = getattr(mod, setting)
# 将我们配置的属性添加到settings配置文件中, 或者覆盖掉
# Django默认的配置属性.
setattr(self, setting, setting_value)
self._explicit_settings.add(setting)

到了这, 开头的问题也就解决了. 来一句话总结, 就是Django先加载自己的配置文件, 然后再加载用户的配置文件覆盖掉默认的属性, 保存到一个settings延时加载的对象中. 配置文件大写的原因也只是因为源码只处理全大写的属性而已.

参考

  1. http://www.hongweipeng.com/index.php/archives/1370/

  2. https://blog.csdn.net/qq_24551305/article/details/96486835

Django的settings文件部分源码分析的更多相关文章

  1. DRF框架(一)——restful接口规范、基于规范下使用原生django接口查询和增加、原生Django CBV请求生命周期源码分析、drf请求生命周期源码分析、请求模块request、渲染模块render

    DRF框架    全称:django-rest framework 知识点 1.接口:什么是接口.restful接口规范 2.CBV生命周期源码 - 基于restful规范下的CBV接口 3.请求组件 ...

  2. python-flask-配置文件的源码分析

    方式一:app.config['xx'] = 'xxx'源码分析:#第1步:class Flask(_PackageBoundObject):    self.config = self.make_c ...

  3. Django——基于类的视图源码分析 二

    源码分析 抽象类和常用视图(base.py) 这个文件包含视图的顶级抽象类(View),基于模板的工具类(TemplateResponseMixin),模板视图(TemplateView)和重定向视图 ...

  4. Django REST framework —— 认证组件源码分析

    我在前面的博客里已经讲过了,我们一般编写API的时候用的方式 class CoursesView(ViewSetMixin,APIView): pass 这种方式的有点是,灵活性比较大,可以根据自己的 ...

  5. Django(60)Django内置User模型源码分析及自定义User

    前言 Django为我们提供了内置的User模型,不需要我们再额外定义用户模型,建立用户体系了.它的完整的路径是在django.contrib.auth.models.User. User模型源码分析 ...

  6. django的RestFramework模块的源码分析

    一.APIView源码分析 查看源码的前提要知道,找函数方法必须先在自己的类中找,没有再往父类找,一层一层网上找,不能直接按ctrl点击 在我们自己定义的类中没有as_view方法的函数,所以肯定是继 ...

  7. Django rest framework框架——APIview源码分析

    一.什么是rest REST其实是一种组织Web服务的架构,而并不是我们想象的那样是实现Web服务的一种新的技术,更没有要求一定要使用HTTP.其目标是为了创建具有良好扩展性的分布式系统. 可用一句话 ...

  8. Django(55)GenericAPIView源码分析

    源码分析 GenericAPIView继承自APIView,也就是在APIView基础上再做了一层封装,源码如下: class GenericAPIView(views.APIView): query ...

  9. Django REST framework —— 权限组件源码分析

    在上一篇文章中我们已经分析了认证组件源码,我们再来看看权限组件的源码,权限组件相对容易,因为只需要返回True 和False即可 代码 class ShoppingCarView(ViewSetMix ...

随机推荐

  1. 20191106-使用pyttsx对文本进行语音输出

    实现pyttsx文字转语音的开源库调用 参考: 1.  https://blog.csdn.net/liuyubo23/article/details/83990532 2.  https://www ...

  2. Angular开发规范

    目录  一.         前言 1.1.       规范目的 1.2.       局限性 二.         文件规范 2.1.       文件结构约定 2.2.       单一职责原则 ...

  3. python设置环境变量(临时和永久)

    设置临时环境变量 import os # 设置环境变量 os.environ['WORKON_HOME']="value" # 获取环境变量方法1 os.environ.get(' ...

  4. [考试反思]0926csp-s模拟测试52:审判

    也好. 该来的迟早会来. 反思再说吧. 向下跳过直到另一条分界线 %%%cbx也拿到了他的第一个AK了呢. 我的还是遥不可及. 我恨你,DeepinC. 我恨透你了.你亲手埋葬所有希望,令我无比气愤. ...

  5. 基于xposed Hook框架实现个人免签支付方案

    我的个人网站如何实现支付功能? 想必很多程序员都有过想开发一个自己的网站来获得一些额外的收入,但做这件事会遇到支付这个问题.目前个人网站是无法实现支付功能的. 今天我就给大家分享一下我的实现方案:&l ...

  6. python机器学习——使用scikit-learn训练感知机模型

    这一篇我们将开始使用scikit-learn的API来实现模型并进行训练,这个包大大方便了我们的学习过程,其中包含了对常用算法的实现,并进行高度优化,以及含有数据预处理.调参和模型评估的很多方法. 我 ...

  7. python——直接插入排序

    直接插入排序 直接插入排序原理 直接插入排序过程 代码 时间复杂度分析 排序稳定性

  8. T-SQL Part IV: ORDER BY

    ORDER BY 返回一个Cursor,并不返回结果集.而试图将Cursor作为输入将产生了错误. 所以,下列的SQL语句将产生错误: SELECT VerID, IsComplete VerID, ...

  9. Tsx写一个通用的button组件

    一年又要到年底了,vue3.0都已经出来了,我们也不能一直还停留在过去的js中,是时候学习并且在项目中使用一下Ts了. 如果说jsx是基于js的话,那么tsx就是基于typescript的 废话也不多 ...

  10. 力扣(LeetCode)旋转字符串 个人题解

    给定两个字符串, A 和 B. A 的旋转操作就是将 A 最左边的字符移动到最右边. 例如, 若 A = 'abcde',在移动一次之后结果就是'bcdea' .如果在若干次旋转操作之后,A 能变成B ...