1.flask写cbv
1.1 cbv模板
之前我们都是写fbv,现在我们写cbv,这样可以把get请求写在一个视图类中:
from flask import Flask, request
from flask.views import View, MethodView
app = Flask(__name__)
'''每次修改之后项目会自己启动'''
app.debug = True
class IndexView(MethodView):
'''在这里不用再把request加入形参,直接使用全局的request就可以'''
def get(self):
print(request.method)
return 'get请求'
def post(self):
print(request.method)
return 'post 请求'
'''endpoint是别名,as_view()括号内也要写别名,后面详解'''
app.add_url_rule('/index',endpoint='index',view_func=IndexView.as_view('index'))
if __name__ == '__main__':
app.run()
1.2 cbv源码分析
1.首先我们需要从as_view()源码中分析,点进原码:
def as_view(
cls, name: str, *class_args: t.Any, **class_kwargs: t.Any
) -> ft.RouteCallable:
def view(**kwargs: t.Any) -> ft.ResponseReturnValue:
self = view.view_class( # type: ignore[attr-defined]
*class_args, **class_kwargs
)
# 不管走if分支还是else分支,都会返回一个current_app.ensure_sync(self.dispatch_request)(**kwargs)。这句代码其实是在执行self.dispatch_request,只不过是异步执行
return current_app.ensure_sync(self.dispatch_request)(**kwargs)
return view
2.当请求来了之后,执行view(),本质还是在执行self.dispatch_request,所以我们现在需要找到dispatch_request源码(我们直接点进去发现什么都没有,这是因为我们直接点进去的是View下的dispatch_request源码,而我们的self是视图类的对象,查找顺序是:对象名称空间(没有)>>>视图类名称空间(没有)>>>MethodView名称空间(找到了)):
def dispatch_request(self, **kwargs: t.Any) -> ft.ResponseReturnValue:
meth = getattr(self, request.method.lower(), None)
'''meth通过反射拿到了视图类中的post或者get,此时的meth就是get或者post'''
if meth is None and request.method == "HEAD":
meth = getattr(self, "get", None)
'''异步执行了get()或者post()'''
return current_app.ensure_sync(meth)(**kwargs)
1.3 as_view()参数分析
当我们在as_view()中不传参数时,项目无法启动。所以as_view()中必须要传参数。这是因为as_view()的返回值是view,所以如果我们不传参数,所有的返回值都是view,结果一样那么必然会报错。
view源代码:
'''view中的形参**kwargs使用了其外层函数as_view中的参数,所以这是一个闭包函数'''
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)
...
view.view_class = cls # type: ignore
view.__name__ = name
view中的形参使用了as_view()中的参数,所以这是一个闭包函数。view.name = name,view就是闭包函数内的view,这句代码是修改了函数的名字。
拓展:当我们print(函数名.name)拿到的是一个函数名,当然我们也可以自己修改其名称,再次打印其名字就是修改之后的:
def index():
pass
print(index.__name__) # index
index.__name__ = 'add'
print(index.__name__) # add
紧接上面,其实我们可以简写生成路由代码:
app.add_url_rule('/index',view_func=IndexView.as_view('index'))
# 等同于:
app.add_url_rule('/index',view_func=view)
所以如果我们不传endpoint参数,并且不在as_view()中传参数的话,那么所有的view都是一个,就会报错。
下面我们来看endpoint和as_view()中参数的关系,首先查看add_url_rule源码:
@setupmethod
def add_url_rule(
self,
rule: str,
endpoint: t.Optional[str] = None,
view_func: t.Optional[ft.RouteCallable] = None,
provide_automatic_options: t.Optional[bool] = None,
**options: t.Any,
) -> None:
if endpoint is None:
endpoint = _endpoint_from_view_func(view_func) # type: ignore
查看_endpoint_from_view_func源码:如果endpoint没有传入,执行以下代码,直接返回view_func对应函数的名字
def _endpoint_from_view_func(view_func: t.Callable) -> str:
assert view_func is not None, "expected view func if endpoint is not provided."
return view_func.__name__
标签:endpoint,name,flask,self,request,func,view
From: https://www.cnblogs.com/ERROR404Notfound/p/17283790.html