Flask快速入门(4) — CBV写法与解析
方式一:继承View
from flask import Flask, views
app = Flask(__name__)
class IndexView(views.View):
methods = ['GET']
def dispatch_request(self):
print('index')
return 'Index!'
app.add_url_rule('/index', view_func=IndexView.as_view(name='name'))
if __name__ == '__main__':
app.run()
同django一样,flask中cbv模式时也是需要
1:自定义一个视图函数类,继承flask.view.View
2:执行as_view()方法,只不过在这里as_view必须要有一个参数name。这里为什么要有name呢?
3:在视图函数类中定义dispatch_request方法,为什么必须要写呢该方法呢.
来看下源码怎么执行,顺便解答一下疑惑。首先是视图类执行as_view方法。先从自定义的类查找,没有再查找父类,即View.as_view()
as_view()源码分析
@classmethod # 是一个类方法,此时cls表示自定义的类IndexView
def as_view(cls, name, *class_args, **class_kwargs): # 第一个位置参数没有默认值,所以必须要传
def view(*args, **kwargs): # 请求来了执行
self = view.view_class(*class_args, **class_kwargs) # cls()实例化一个对象
return self.dispatch_request(*args, **kwargs) # 执行dispath_request()方法,如果在自定义类中没有定义该方法,则执行父类的dispatch_request(),点进去其实就是raise NotImplementedError()
if cls.decorators:
view.__name__ = name
view.__module__ = cls.__module__
for decorator in cls.decorators:
"""在这里对decorators循环遍历,然后再将view函数传入执行。其实就是装饰器"""
view = decorator(view)
view.view_class = cls
view.__name__ = name # 给view函数重命名,这样在该方法 return view时返回的是不同的函数名,遵循路由与视图函数一一对应的元则
view.__doc__ = cls.__doc__
view.__module__ = cls.__module__
view.methods = cls.methods
view.provide_automatic_options = cls.provide_automatic_options
return view # 返回的是一个函数名,这样当请求来的时候执行的就是view()函数
总结:
(1)自定义视图类继承flask.views.View时,必须要重写dispatch_request()方法。在该方法中来分发请求方式执行相应的功能
(2)在as_view()中必须要传一个name参数。就是相当于endpoint起了一个别名用于反向解析路由。有endpoint反向解析用endpoint,没有就用name的值
(3)不同的路由不能起相同的别名。应遵循路由与视图函数一一对应的元则,比如这样是不行的
app.add_url_rule('/index', view_func=IndexView.as_view(name='name'))
app.add_url_rule('/login', view_func=IndexView.as_view(name='name'))
方式二:继承MethodView
from flask import Flask,views
app = Flask(__name__)
class IndexView(views.MethodView):
methods = ['GET','POST']
def get(self):
return 'index get method'
def post(self):
return 'index post method'
app.add_url_rule('/index',view_func=IndexView.as_view(name='hello'))
if __name__ == '__main__':
app.run()
从方法一的分析可知,在继承View的自定义视图类中必须要定义dispatch_request方法,在该方法中通过判断请求方式来实现相对应的功能。而在MethodView类中已经帮我们实现了dispatch_request。让我们一起来看一看MethodView的源码:
class MethodView(with_metaclass(MethodViewType, View)):
def dispatch_request(self, *args, **kwargs):
# self是自定义的视图类,判断来的请求方式请否允许并获取
meth = getattr(self, request.method.lower(), None)
if meth is None and request.method == "HEAD":
meth = getattr(self, "get", None)
assert meth is not None, "Unimplemented method %r" % request.method
return meth(*args, **kwargs) # 比如请求方式为get,到这里就是执行get()
(1)执行as_view()方法。其实还是调用的是View里的as_view,上面已经介绍过了,请求来了就执行dispatch_request方法
(2)在自定义类中没有dispatch_request方法,所以执行MethodView中的dispatch_request方法
(3)dispatch_request中判断了请求方式是否允许,以及执行请求
第二种方式已经帮我们封装好了请求分发的功能,所以是比较推荐的使用方式