什么是CBV
FBV(function base view)基于函数的视图
CBV(class base view)基于类的视图
#urls.py
from django.conf.urls import url
from user.views import Index,IndexView
urlpatterns = [
url(r'^fbv/', Index),
url(r'^cbv/', IndexView.as_view()),
]
#views.py
#FBV
def Index(request):
if request.method == "GET":
return HttpResponse("success")
#CBV
class IndexView(View):
def get(self,request):
return HttpResponse("success")
一般都使用CBV,因为类有继承、封装、多态的特性。
CBV请求流程分析
先看下路由,url(r'^cbv/', IndexView.as_view())
,主要看下as_view()
这个类方法。
IndexView
类中并没有定义,查看父类View
中有没有。View
类的定义是在from django.views import View
找到父类的as_view()
方法:
@classonlymethod
def as_view(cls, **initkwargs):
"""
Main entry point for a request-response process.
"""
for key in initkwargs:
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__))
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):
self = cls(**initkwargs)
if hasattr(self, 'get') and not hasattr(self, 'head'):
self.head = self.get
self.request = request
self.args = args
self.kwargs = kwargs
return self.dispatch(request, *args, **kwargs)
view.view_class = cls
view.view_initkwargs = initkwargs
# take name and docstring from class
update_wrapper(view, cls, updated=())
# and possible attributes set by decorators
# like csrf_exempt from dispatch
update_wrapper(view, cls.dispatch, assigned=())
return view
这是一个类方法,最终返回了view这个方法,所以CBV和FBV路由中最终都是指向一个方法。
注意力集中到view(request, *args, **kwargs)
方法上:
def view(request, *args, **kwargs):
self = cls(**initkwargs)
if hasattr(self, 'get') and not hasattr(self, 'head'):
self.head = self.get
self.request = request
self.args = args
self.kwargs = kwargs
return self.dispatch(request, *args, **kwargs)
因为路由中调用是url(r'^cbv/', IndexView.as_view())
,最终调用了父类的as_view()方法中的view()方法,所以该view()方法里的cls指的是调用时的类,即IndexView
,self就是IndexView的实例。
最终返回了self.dispatch(request, *args, **kwargs)
,先去IndexView
类中查看,有没有dispatch()
这个实例方法,发现没有,再去父类View
中查看,dispatch()
源码如下:
def dispatch(self, request, *args, **kwargs):
# Try to dispatch to the right method; if a method doesn't exist,
# defer to the error handler. Also defer to the error handler if the
# request method isn't on the approved list.
if request.method.lower() in self.http_method_names:
handler = getattr(self, request.method.lower(), self.http_method_not_allowed)
else:
handler = self.http_method_not_allowed
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 http.HttpResponseNotAllowed(self._allowed_methods())
根据方法注释可以知道作用是调用正确的方法,如果方法不存在或者方法不在允许列表都会报错。
request.method.lower()
,request.method返回的是大写的请求方式,比如GET
,POST
。转换成小写是因为我们写的请求处理方法都是小写。
http_method_names = ['get', 'post', 'put', 'patch', 'delete', 'head', 'options', 'trace']
这是允许的方法列表。
现在来看下最关键的一句代码:
handler = getattr(self, request.method.lower(), self.http_method_not_allowed)
作用是获取self这个对象的 request.method.lower()属性,如果没有该属性指定调用self.http_method_not_allowed方法处理异常
翻译后:
handler = getattr(self, "get", self.http_method_not_allowed)
上面说过self是IndexView的示例,会优先在IndexView中寻找get属性或方法,等同于self.get
。打印print(type(self.get))
得到<class 'method'>
,说明可以获取到IndexView
中的get()
方法的引用。
所以handler(request, *args, **kwargs)
等同于get(request, *args, **kwargs)
即执行了IndexView
中的get()
方法。
重写父类的dispatch()方法
重写父类dispatch()方法达到类似中间件的效果,每次请求开始和结束都会打印开始和结束
class IndexView(View):
def dispatch(self, request, *args, **kwargs):
print("请求开始")
ret = super(IndexView, self).dispatch(request, *args, **kwargs)
print("请求结束")
return ret
def get(self,request):
return HttpResponse("success")
重写dispatch()
,里面调用的其实还是父类的方法
将dispatch()方法封装到公共类中
为了方便其他类也可以使用,进一步封装到一个公共类中
class Base:
def dispatch(self, request, *args, **kwargs):
print("请求开始")
ret = super(Base, self).dispatch(request, *args, **kwargs)
print("请求结束")
return ret
class IndexView(Base,View):
def get(self,request):
return HttpResponse("success")
此时,再按照CBV的流程走一遍,先找到IndexView
的as_view()
方法,由于是多继承,Base类
中没有,去View类
中继续找,找到后发现最终调用的self.dispatch()
方法。还是那句话,self
指的是IndexView
实例,因此根据多继承关系,从左到右,在Base类
中找到dispatch()
方法,开始执行...
ret = super(Base, self).dispatch(request, *args, **kwargs)
执行到这一句,要调用super(Base, self).ispatch()
,此时该怎么走?
先看下面的多继承super()调用顺序,再回到这里,执行IndexView.mro()
得到[<class 'user.views.IndexView'>, <class 'user.views.Base'>, <class 'django.views.generic.base.View'>, <class 'object'>]
。Base
中的super(Base, self).dispatch(request, *args, **kwargs)
会到View
方法中去执行dispatch()
方法。
多继承中super()的调用顺序
参考这篇博客https://blog.csdn.net/u011318077/article/details/89677240
,复制了里面三个案例代码:
demo1
class Parent(object):
def __init__(self, name):
print('parent的init开始被调用')
self.name = name
print('parent的init结束被调用')
# 新建属性age,继承属性name
class Son1(Parent):
def __init__(self, name, age):
print('Son1的init开始被调用')
self.age = age
# 继承父类中的__init__初始化方法
Parent.__init__(self, name)
print('Son1的init结束被调用')
# 新建属性gender,继承属性name
class Son2(Parent):
def __init__(self, name, gender):
print('Son2的init开始被调用')
self.gender = gender
# 继承父类中的__init__初始化方法
Parent.__init__(self, name)
print('Son2的init结束被调用')
# 继承属性name,age,gender
class Grandson(Son1, Son2):
def __init__(self, name, age, gender):
print('Grandson的init开始被调用')
# 继承父类的初始化方法
Son1.__init__(self, name, age)
Son2.__init__(self, name, gender)
print('Grandson的init结束被调用')
gs = Grandson('grandson', 12, '男')
>>>
Grandson的init开始被调用
Son1的init开始被调用
parent的init开始被调用
parent的init结束被调用
Son1的init结束被调用
Son2的init开始被调用
parent的init开始被调用
parent的init结束被调用
Son2的init结束被调用
Grandson的init结束被调用
Parent的初始化方法会被调用两次
demo2
class Parent(object):
def __init__(self, name, *args, **kwargs): # 为避免多继承报错,使用不定长参数,接受参数
print('parent的init开始被调用')
self.name = name
print('parent的init结束被调用')
class Son1(Parent):
def __init__(self, name, age, *args, **kwargs): # 为避免多继承报错,使用不定长参数,接受参数
print('Son1的init开始被调用')
self.age = age
super().__init__(name, *args, **kwargs) # 为避免多继承报错,使用不定长参数,接受参数
print('Son1的init结束被调用')
class Son2(Parent):
def __init__(self, name, gender, *args, **kwargs): # 为避免多继承报错,使用不定长参数,接受参数
print('Son2的init开始被调用')
self.gender = gender
super().__init__(name, *args, **kwargs) # 为避免多继承报错,使用不定长参数,接受参数
print('Son2的init结束被调用')
class Grandson(Son1, Son2):
def __init__(self, name, age, gender):
print('Grandson的init开始被调用')
# 多继承时,相对于使用类名.__init__方法,要把每个父类全部写一遍
# 而super只用一句话,执行了全部父类的方法,这也是为何多继承需要全部传参的一个原因
# super(Grandson, self).__init__(name, age, gender)
super().__init__(name, age, gender)
print('Grandson的init结束被调用')
print(Grandson.mro())
gs = Grandson('grandson', 12, '男')
>>>
(<class '__main__.Grandson'>, <class '__main__.Son1'>, <class '__main__.Son2'>, <class '__main__.Parent'>, <class 'object'>)
Grandson的init开始被调用
Son1的init开始被调用
Son2的init开始被调用
parent的init开始被调用
parent的init结束被调用
Son2的init结束被调用
Son1的init结束被调用
Grandson的init结束被调用
多继承使用super(),每个父类都只会被调用一次。Grandson.mro()可以查看多继承中的执行顺序
demo3
class Parent(object):
def __init__(self, name, *args, **kwargs): # 为避免多继承报错,使用不定长参数,接受参数
print('parent的init开始被调用')
self.name = name
print('parent的init结束被调用')
class Son1(Parent):
def __init__(self, name, age, *args, **kwargs): # 为避免多继承报错,使用不定长参数,接受参数
print('Son1的init开始被调用')
self.age = age
super().__init__(name, *args, **kwargs) # 为避免多继承报错,使用不定长参数,接受参数
print('Son1的init结束被调用')
class Son2(Parent):
def __init__(self, name, gender, *args, **kwargs): # 为避免多继承报错,使用不定长参数,接受参数
print('Son2的init开始被调用')
self.gender = gender
super().__init__(name, *args, **kwargs) # 为避免多继承报错,使用不定长参数,接受参数
print('Son2的init结束被调用')
class Grandson(Son1, Son2):
def __init__(self, name, age, *args, **kwargs):
print('Grandson的init开始被调用')
# 多继承时,相对于使用类名.__init__方法,要把每个父类全部写一遍
# 而super只用一句话,执行了全部父类的方法,这也是为何多继承需要全部传参的一个原因
super(Son1, self).__init__(name, age, *args, **kwargs)
print('Grandson的init结束被调用')
print(Grandson.mro())
gs = Grandson('grandson', 12, '男')
>>>
(<class '__main__.Grandson'>, <class '__main__.Son1'>, <class '__main__.Son2'>, <class '__main__.Parent'>, <class 'object'>)
Grandson的init开始被调用
Son2的init开始被调用
parent的init开始被调用
parent的init结束被调用
Son2的init结束被调用
Grandson的init结束被调用
写成super(Grandson, self).init(name, age, gender),则找Grandson下一个开始执行,与demo2结果一样
写成super(Son1, self).init(name, age, *args, **kwargs), 则找Son1下一个,从Son2开始执行
多继承,经典类中搜索顺序是深度优先,新式类中是广度优先。super只在python3中才能使用,super不是直接找父类,而是根据调用者的节点位置(节点表示super(Grandson, self).__init__(name, age, gender)中的self,即Grandson对象)的广度优先顺序来的
网友评论