【4.0】Flask框架之CBV
【一】基本使用
from flask import Flask, render_template
# template_folder 默认就是当前文件夹下的 templates 可以不写
app = Flask(__name__, template_folder='templates')
# FBV : 基于函数的视图
@app.route('/index', methods=['GET'])
def index():
return 'ok'
# CBV : 基于类的视图
# CBV : 需要继承基类 MethodView , MethodView 继承了 View
from flask.views import View, MethodView
# CBV 视图函数
class Home(MethodView):
def get(self):
return render_template('home.html')
def post(self):
return "POST"
# 注册路由
app.add_url_rule('/home', endpoint='home', view_func=Home.as_view('home'))
if __name__ == '__main__':
app.run()
【二】CBV 函数加装饰器
# -*-coding: Utf-8 -*-
# @File : 01CBV .py
# author: Chimengmeng
# blog_url : https://www.cnblogs.com/dream-ze/
# Time:2023/8/24
from flask import Flask, render_template
# template_folder 默认就是当前文件夹下的 templates 可以不写
app = Flask(__name__, template_folder='templates')
# 登录装饰器
def auth(func):
def inner(*args, **kwargs):
res = func(*args, **kwargs)
return res
return inner
# FBV : 基于函数的视图
@app.route('/index', methods=['GET'], endpoint='index')
@auth
def index():
return 'ok'
# CBV : 基于类的视图
# CBV : 需要继承基类 MethodView , MethodView 继承了 View
from flask.views import View, MethodView
# CBV 视图函数
class Home(MethodView):
# MethodView 有一个类属性 decorators 可以存放装饰器
# 如果有多个可以直接 逗号隔开
decorators = [auth]
# 可以控制整个视图函数的请求方式
methods = ['GET']
def get(self):
return render_template('home.html')
def post(self):
return "POST"
# 注册路由
# as_view('home') 中的 home 其实就是 endpoint
app.add_url_rule('/home', endpoint='home', view_func=Home.as_view('home'))
if __name__ == '__main__':
app.run()
为什么decorators = [auth] 能加装饰器
app.add_url_rule('/home', view_func=view内存地址)
用装饰器一直在装饰 view内存地址 ,所以,以后执行,就是有装饰器的view,装饰器代码会走
@auth def view(**kwargs): return self.dispatch_request(**kwargs)
- 等价于
view = auth(view)
- view_func=Home.as_view('home') home 是 endpoint,就是路由别名
- app.add_url_rule('/home', view_func=Home.as_view('home'))
- add_url_rule---》endpoint没传---》会执行endpoint = _endpoint_from_view_func(view_func)
- 取函数名作为 endpoint 的值
- view_func是 加了一堆装饰器的view函数---》它的名字应该是装饰器的名字--》但是
view.__name__ = name
# name 就是home- 所以endpoint就是你传的home
- 如果传了endpoint,就是传的那个,那as_view('home')就没作用了,但是也必须要传
【三】CBV 源码分析
- 入口
as.view()
def as_view(
cls, name: str, *class_args: t.Any, **class_kwargs: t.Any
) -> ft.RouteCallable:
if cls.init_every_request:
def view(**kwargs: t.Any) -> ft.ResponseReturnValue:
self = view.view_class( # type: ignore[attr-defined]
*class_args, **class_kwargs
)
# 支持异步操作
return current_app.ensure_sync(self.dispatch_request)(**kwargs)
else:
self = cls(*class_args, **class_kwargs)
def view(**kwargs: t.Any) -> ft.ResponseReturnValue:
return current_app.ensure_sync(self.dispatch_request)(**kwargs)
# 装饰器列表
if cls.decorators:
view.__name__ = name
view.__module__ = cls.__module__
# 遍历每一个装饰器
for decorator in cls.decorators:
# 装饰器语法糖干的事:
#把被装饰的函数当参数传入到装饰器,返回结果赋值给被装饰的函数,一个个用装饰器包装view
view = decorator(view)
view.view_class = cls # type: ignore
view.__name__ = name
view.__doc__ = cls.__doc__
view.__module__ = cls.__module__
view.methods = cls.methods # type: ignore
view.provide_automatic_options = cls.provide_automatic_options # type: ignore
# 返回 view 函数
return view
view
if cls.init_every_request:
def view(**kwargs: t.Any) -> ft.ResponseReturnValue:
self = view.view_class( # type: ignore[attr-defined]
*class_args, **class_kwargs
)
# 支持异步操作
return current_app.ensure_sync(self.dispatch_request)(**kwargs)
else:
self = cls(*class_args, **class_kwargs)
def view(**kwargs: t.Any) -> ft.ResponseReturnValue:
return current_app.ensure_sync(self.dispatch_request)(**kwargs)
- 调用了
dispatch_request
方法- 我们发现这是一个抛异常的函数
def dispatch_request(self) -> ft.ResponseReturnValue:
"""The actual view function behavior. Subclasses must override
this and return a valid response. Any variables from the URL
rule are passed as keyword arguments.
"""
raise NotImplementedError()
- 其实我们应该去找 父类 的
dispatch_request
方法MethodView
的dispatch_request
方法
def dispatch_request(self, **kwargs: t.Any) -> ft.ResponseReturnValue:
# request.method.lower() 获取到 method 的小写 假设是get请求
# meth 就是 CBV 中以get命名的方法
meth = getattr(self, request.method.lower(), None)
# If the request method is HEAD and we don't have a handler for it
# retry with GET.
#
if meth is None and request.method == "HEAD":
meth = getattr(self, "get", None)
assert meth is not None, f"Unimplemented method {request.method!r}"
# 执行方法
return current_app.ensure_sync(meth)(**kwargs)
本文来自博客园,作者:Chimengmeng,转载请注明原文链接:https://www.cnblogs.com/dream-ze/p/17659501.html