一、FBV
FBV(Function Base Views) 基于函数的视图;在视图里面通过函数来处理请求、响应请求。在之前的Django学习中我们一直使用的方式,这里不再赘述。
二、CBV
2.1 简介
CBV(Class Base Views)基于类的视图;在视图里通过面向对象来处理、响应请求。
Python是一个面向对象的编程语言,如果只用函数来开发,有很多面向对象的优点就错失了(继承、封装、多态)。所以Django在后来加入了Class-Based-View。可以让我们用类写View。这样做的优点主要下面两种:
- 提高代码的复用性,可以使用面向对象的技术。
- 根据不同的类方法处理不同的HTTP请求,而不是通过很多if判断,提高代码可读性。
如果要写一个处理GET请求的view,FBV是这样写的:
from django.shortcuts import HttpResponse
def index(request):
if request.method == 'GET':
return HttpResponse('this is index')
urlpatterns = [
path('index/', views.index)
]
使用CBV是这样写的:
from django.shortcuts import HttpResponse,
from django.views import View
class My_index(View):
def get(self, request):
return HttpReponse('this is index')
urlpatterns = [
path('index/', views.My_index.as_view()),
]
从浏览器发送过来的请求会被Django根据URL传递给某个对应的函数,而不是class。针对这个问题,CBV提供了一个as_view()类方法,为何调用这个类方法就能让我们类里面的函数接收请求了呢,我们来看看它的源码。
2.1 源码解析
根据之前的操作就知道,我们在路由里面指向的函数不用我们手动调用,Django会自动调用并传递一个请求过去,那么上面也看到了,这个方法是我们手动调用的,这是因为我们需要让Django调用的是view函数,并且把请求给这个函数。
urlpatterns = [
path('index/', views.index.as_view())
# 手动调用后会拿到view方法的返回值。变成了以下的样子
path('index/', views.index.view)
# 那么此时Django会帮助我们调用一次这个方法,并且传递一个request请求过去。
]
2.2 view方法解析
那么我们再看看view方法干了些什么事情:只看红框内容即可
- 首先是根据cls实例化出一个对象了(这cls就是我们在视图内定义的类)
- 将request请求给这个对象作为属性
- 而args、kwargs则是防止有名分组或无名分组,用于接收分组产生的值的。也一同赋给这个对象。
- 调用这个对象下面的dispatch方法,并将Django传递的参数一并传递过去了
2.3 dispatch方法解析
再分析一下dispatch方法的作用:只关注红框内容即可
request.method.lower()
将请求的方法变成小写的字符串
主要就是判断:发送请求的方法是否符合正常HTTP请求
http_method_names = ['get', 'post', 'put', 'patch', 'delete', 'head', 'options', 'trace']
如果符合的话:使用反射通过拿到的字符串去对象里查找相同名称的函数。比如:
class index(View):
def get(self,request):
return HttpResponse('from GET')
self = index类实例化的对象,并且拥有request、args、kwargs等属性
# 等同于:如果request.method.lower()是get的话,拿到get方法的函数对象
handle = getattr(self,'get',None) # 第三个参数则是没有找到get方法或属性返回的。
handle = get函数对象
handle(request,*args,**kwargs) # 等同于:get(self,request,*args,**kwargs)
调用时将request请求、一些额外参数都传递给了我们定义的类里面的get方法。
演示:不定义任何处理请求的函数
class index(View):
pass
浏览器得不到Django响应的任何数据!
get方法需要返回一个HttpResponse对象。没有返回的话,则页面报错
class index(View):
def get(self,request):
print('-------')
注意:如果类里面没有定义处理对应请求的函数,浏览器得不到响应,专门处理某个请求的函数名一定要与请求名一致。
总结:
我们首先得知道使用CBV目的是什么、又或者Django引入CBV目的是什么。其实在早期版本使用Django普遍使用的都是FBV编写模式,甚至认为FBV比CBV更好用,实则不然。Python中一大特性便是面向对象,CBV是通过面向对象来实现视图的,而它相对于函数而言,具备了多种特性。而利用多态性更容易从宏观层面上将项目内的比较通用的功能抽象出来。
而CBV的实现通过上序源码的分析也可以看出来,大致就是通过URL执行类里面的view方法,再通过其内部的dispatch方法进行分发处理,将浏览器请求交给我们的类里面对应的方法进行处理。
三、settings源码解析
3.1 简介
settings.py是整个Django项目的配置文件、我们可以在里面配置连接数据库、配置静态文件、配置Django语言显示等等。
但其实Django有两个配置文件:
- 一是暴露给用户可以自定义配置的文件,也就是我们所见的settings.py文件
- 另一个则是Django项目的默认配置文件,当用户不做任何配置的时候就会加载默认的配置文件
我们可以打开这个默认配置文件,与我们可以配置的settings.py文件做一个对比,而这个默认配置文件也可以称为:全局配置文件
from django.conf import global_settings
在全局配置文件内显示的更加全面,settings.py文件可供配置的不多,但也可以满足我们的基本需求了,如上面的语言设置:在全局配置内可以告诉我们,在settings.py里面设置什么值可以调整成什么语言。
实例:我们设置为zh-hans简体中文,再运行一下Django。进行后台管理界面
3.2 源码解析
我们会发现settings.py文件里面所有变量名都是大写的,包括我们配置静态文件时增加的一个变量名也是需要大写才能生效的,这是settings模块的一个机制,我们且来看看其源码如何规定的。
settings.py文件的规则,以及如何在全局生效的都在这个模块下面可以看到。
from django.conf import settings
点进去之后发现其指向了一个类,我们继续向后探索
settings = LazySettings()
进去之后我们主要关注的代码:_setup方法在配置文件加载后会自动被调用
class LazySettings(LazyObject):
def _setup(self, name=None):
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))
self._wrapped = Settings(settings_module)
那么我们了解一下_setup方法做了些什么事情!
# 在系统全局字典内获取一个名为:ENVIRONMENT_VARIABLE的value值
settings_module = os.environ.get(ENVIRONMENT_VARIABLE)
再来看一看:ENVIRONMENT_VARIABLE
是个啥
ENVIRONMENT_VARIABLE = "DJANGO_SETTINGS_MODULE"
而settings_module就需要在系统全局大字典内获取key值为:DJANGO_SETTINGS_MODULE
的value值
而这个key会在我们使用Django的入口文件时产生:也就是manage.py
内
而这个值就是我们的settings.py
文件的路径。再次回到上面,此时我们可以得知:settings_module
变量会得到:settings.py
文件的路径。
再次向下解析,那个if判断不用看,因为我们值本身就是True,not也就不会执行了。
此时就运行到了这一行代码,可以发现它调用了一个类,并把我们settings.py
文件的路径传了过去
self._wrapped = Settings(settings_module)
再根据这个Settings类,我们再向下调查,关注重点代码!
class Settings:
def __init__(self, settings_module): # 'mysite.settings'
for setting in dir(global_settings): # 获取全局配置文件里面所有的变量名
if setting.isupper(): # 校验是否是纯大写
setattr(self, setting, getattr(global_settings, setting))
# 通过反射,根据变量名,获取全局配置里面对应变量名的值。
# 给Settings对象添加全局配置文件中所有的配置信息
self.SETTINGS_MODULE = settings_module # mysite.settings
mod = importlib.import_module(self.SETTINGS_MODULE)
# 模块反射,导入我们settings.py文件;相当于:from mysite import settings
''' 不关注
tuple_settings = (
"INSTALLED_APPS",
"TEMPLATE_DIRS",
"LOCALE_PATHS",
)
self._explicit_settings = set()'''
for setting in dir(mod): # 获取我们settings.py配置文件内的所有变量名
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)
# Settings对象已经被添加了所有全局配置信息
# 那么此时再添加我们的settings.py文件里面的所有配置
# 那么必定会与全局配置添加的有所重合,如果重合则更改,没重合则新增
# 不关注
'''self._explicit_settings.add(setting)'''
最后也就是,Django加载配置会先加载全局配置文件,再加载settings.py文件里面的配置。相当于将它们总和到一起,如果两个配置文件有名称冲突,则以settings.py文件为主。
from django.conf import settings
可以发现,我们自己的配置与全局配置文件里面的内容都在这个settings模块里面。
如果本文对您有帮助,别忘一键3连,您的支持就是笔者最大的鼓励,感谢阅读!
下一章节传送门:模板层语法
技术小白记录学习过程,有错误或不解的地方请指出,如果这篇文章对你有所帮助请
点赞 收藏+关注
子夜期待您的关注,谢谢支持!