0. django视图有两种写法,一般我们用FBV,今天我们揭开CBV的面纱
总结:
# 1. 你在路由中写一个as_view,他实际上会自动执行,并返回一个内层函数地址,
# 2. 等请求来了,django自动加括号执行View方法,并且把当前的request传入
# 3. 然后实例化一个当前类对象,在进行一些判断,
# 4. 给他封装一下属性-->判断结束执行dispatch,返回一个handler方法,
# 5. handler方法,只有两种情况:1.你的get视图/post视图...
# 2. 报错信息
# 6. 最终在封装一些属性,然后把view返回
一. 导入并写一个简单的url&视图
# 提前说一下,django的路由规定,路由的第二个参数必须放一个函数的内存地址,或者一个include
# as_view()执行完以后肯定返回一个内存地址
path('text_view/', views.TextView.as_view()),
from django.views import View
class TextView(View):
"""cbv"""
def get(self, request):
return HttpResponse('ok')
二. 按住ctrl键,鼠标点as_view,点进去
三. 源码分析(他的精髓我基本上都有注释)
1. classonlymethod注释
@classonlymethod
# 1. classonlymethod 只是继承了classmethod,本质上还是classmethod,只是比classmethod多了一点功能
# 2. classmethod-->类的绑定方法,类调用的时候,会把当前类,作为第一个参数传进去
2. as_view--->setup注释
@classonlymethod
# 1. classonlymethod 只是继承了classmethod,本质上还是classmethod,只是比classmethod多了一点功能
# 2. classmethod-->类的绑定方法,类调用的时候,会把当前类,作为第一个参数传进去
def as_view(cls, **initkwargs):
# 1. initkwargs--->一个普通的形参,只是名字好看一点,把它当成**kwargs即可
"""Main entry point for a request-response process."""
for key in initkwargs:
# 1. 这一步循环取值并判断,其实对我们来讲意义不大,只要知道,as_view()支持以字典的形式传参,例如:as_view({'get':'xxx'})
# 2. 如果该形参有值,进行循环取值,并判断,如果传的值在http_method_names列表中的话,抛异常
if key in cls.http_method_names:
raise TypeError("You tried to pass in the %s method name as a "
"keyword argument to %s(). Don't do that."
% (key, cls.__name__))
# 1. 进行判断,比if高级了一点,判断当前传入的值,如果不在当前类中(当前类没有,去其父类,也就是,我们继承的View中找),抛异常
if not hasattr(cls, key):
raise TypeError("%s() received an invalid keyword %r. as_view "
"only accepts arguments that are already "
"attributes of the class." % (cls.__name__, key))
def view(request, *args, **kwargs):
# 1. 利用闭包的思想(闭包的最基本的概念:在函数内部定义的变量对外部有调用)
# 2. 提前说一下,as_view最终返回一个view的内存地址,
# 3. 我们一开始就说了,django路由规定,路由的第二个参数必须放一个函数的内存地址/include,这个符合django的路由规定
# 4. 绕了一圈,我们知道,原来as_view(),加括号执行后,居然返回一个内层函数view,并且as_view支持以字典的方式传参
self = cls(**initkwargs)
# 1. cls-->由于as_view是一个类的绑定方法,类调用的时候,会把类传进来,
# 2. 那么cls就是我们当前调用的类--->TextView
# 3. 他这一步就是把我们的TextView类加括号,实例化成对象(我们下列用TV对象代替他)
if hasattr(self, 'get') and not hasattr(self, 'head'):
# 1. 判断get是TV对象对象的方法
# 2. head不是TV对象对象的方法
self.head = self.get
# 如果成立给TV对象增加一个实例属性,{'head':'get'}
self.setup(request, *args, **kwargs)
# 1. 调用TV对象的setup方法(TV对象中没有,去View中去找)
3. setup方法的注释
self.setup(request, *args, **kwargs)
# 1. 调用TV对象的setup方法(TV对象中没有,去View中去找)
def setup(self, request, *args, **kwargs):
"""Initialize attributes shared by all view methods."""
# self-->依旧是TV对象,兄弟们千万别迷了
# 以下操作,就是给TV对象添加实例化属性{'request':request,'args':args,'kwargs':kwargs}
# 只是放在一个方法中写了
# 这里粗略介绍一下函数跟方法的区别
# 函数,所有参数手动传
# 方法,第一个参数自动把当前调用者传进来
self.request = request
self.args = args
self.kwargs = kwargs
4. as_view--->setup注释--->dispatch注释(精髓)
self.setup(request, *args, **kwargs)
# 1. 调用TV对象的setup方法(TV对象中没有,去View中去找)
if not hasattr(self, 'request'):
# 1. 老套路,判断request是否是TV对象的属性
# 2. 不是抛异常
raise AttributeError(
"%s instance has no 'request' attribute. Did you override "
"setup() and forget to call super()?" % cls.__name__
)
# 1. 是的话,执行dispatch方法,
# 2. 说一下执行流程,首先去对象里面找(没有)--在去类中找(没有)--在去其父类中找(没有)--父类的父类,一直找到根(还没有报错,xxx没有这个属性)
# 最终返回 handler方法
# 3. handler-->存的是你在视图中写的 get/post...
# 4. 或者是 异常方法
return self.dispatch(request, *args, **kwargs)
def dispatch(self, request, *args, **kwargs):
if request.method.lower() in self.http_method_names:
# 1. 把request.method-->GET/POST...,变成小写,判断是否在列表中
handler = getattr(self, request.method.lower(), self.http_method_not_allowed)
# 2. 反射,从TV对象中找到该方法,如果找不到,执行self.http_method_not_allowed
else:
handler = self.http_method_not_allowed
# 3. 最后返回handler
# 4. handler-->存的是你在视图中写的 get/post...
# 5. 或者是 异常方法
return handler(request, *args, **kwargs)
def http_method_not_allowed(self, request, *args, **kwargs):
# 记录日志,抛异常
logger.warning(
'Method Not Allowed (%s): %s', request.method, request.path,
extra={'status_code': 405, 'request': request}
)
return HttpResponseNotAllowed(self._allowed_methods())
5. 最后
# 1. 是的话,执行dispatch方法,
# 2. 说一下执行流程,首先去对象里面找(没有)--在去类中找(没有)--在去其父类中找(没有)--父类的父类,一直找到根(还没有报错,xxx没有这个属性)
# 最终返回 handler方法
# 3. handler-->存的是你在视图中写的 get/post...
# 4. 或者是 异常方法
return self.dispatch(request, *args, **kwargs)
view.view_class = cls
view.view_initkwargs = initkwargs
# 1. 执行完,我大概说一下,这个view.view_class = cls是什么意思
# 2. view是函数的名称,因为一切皆对象的原因,函数也是一个对象,
# 3. 如果是对象,对象就支持 句点符 设值
# 4. 这个就是给view添加两个属性
# take name and docstring from class
update_wrapper(view, cls, updated=())
# 1. 对我们来讲愿意不大,
# and possible attributes set by decorators
# like csrf_exempt from dispatch
update_wrapper(view, cls.dispatch, assigned=())
# 1. 对我们来讲愿意不大,
# 1. 最终返回view,到此CBV的精髓全部介绍完毕
# 2. 在总结一下:
# 你在路由中写一个as_view,他实际上会自动执行,并返回一个内层函数地址,
# 等请求来了,django自动加括号执行View方法,并且把当前的request传入
# 然后实例化一个当前类对象,在进行一些判断,
# 给他封装一下属性-->判断结束执行dispatch,返回一个handler方法,
# handler方法,只有两种情况:1.你的get视图/post视图...
# 2. 报错信息
# 最终在封装一些属性,然后把view返回
return view