flask-

发布时间 2023-04-03 17:42:21作者: ERROR404Notfound

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()

image
image

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都是一个,就会报错。
image
下面我们来看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__