Flask中的CBV
学完django, 在学flask, 感觉自己在复习django, django真是包罗万象,
学习flask前, 先吹捧一下django, 也是没谁了,
今天来学习一个flask的CBV
什么是CBV
说到FBV就是function, 说到CBV就是class
这样说没毛病, 但是应该学会选择什么时候使用FBV, 什么时候使用CBV
FBV简单, 小巧, 当不涉及到复杂的逻辑时可以使用FBV
CBV 灵活, 为什么说灵活呢, 因为, 类的封装, 继承, 多态, 你说灵活不灵活
定义一个flask中的CBV视图
class LoginView(views.MethodView): def get(self): return "LoginView get" def post(self): return "LoginView post"
CBV有了, 路由在哪里, 之前FBV都是使用装饰器来声明一个路由的, 现在怎么办?
其实只要看一看@app.route()的源码就会知道该怎么做了.
@app.route()源码浅解
def route(self, rule, **options): def decorator(f): """ :param f: 视图函数 :return: 内部函数 decorator """ endpoint = options.pop('endpoint', None) # 从参数中弹出endpoint, 没有的话就是None self.add_url_rule(rule, endpoint, f, **options) # 调用这个函数, 将路由规则, endpoint, 视图函数传了进去, 其实这里就是去添加路由和视图的对应关系了 return f return decorator
看看是怎么添加路由和视图的对应关系的
@setupmethod def add_url_rule(self, rule, endpoint=None, view_func=None, provide_automatic_options=None, **options): """ :param rule: 视图函数的route装饰器中定义的路由规则 :param endpoint: 从option中取出的endpoint :param view_func: 视图函数 :param provide_automatic_options: :param options: :return: """ if endpoint is None: # endpoint如果为None endpoint = _endpoint_from_view_func(view_func) # 将试图视图函数传了进去, 返回视图函数的__name__ options['endpoint'] = endpoint # 重新赋值endpoint, 这时endpoint有可能等于视图函数的__name__, 或还是之前的值 methods = options.pop('methods', None) # 将请求方式弹出 if methods is None: methods = getattr(view_func, 'methods', None) or ('GET',) if isinstance(methods, string_types): raise TypeError('Allowed methods have to be iterables of strings, ' 'for example: @app.route(..., methods=["POST"])') methods = set(item.upper() for item in methods) required_methods = set(getattr(view_func, 'required_methods', ())) if provide_automatic_options is None: provide_automatic_options = getattr(view_func, 'provide_automatic_options', None) if provide_automatic_options is None: if 'OPTIONS' not in methods: provide_automatic_options = True required_methods.add('OPTIONS') else: provide_automatic_options = False methods |= required_methods rule = self.url_rule_class(rule, methods=methods, **options) rule.provide_automatic_options = provide_automatic_options self.url_map.add(rule) if view_func is not None: old_func = self.view_functions.get(endpoint) if old_func is not None and old_func != view_func: raise AssertionError('View function mapping is overwriting an ' 'existing endpoint function: %s' % endpoint) self.view_functions[endpoint] = view_func # 重要的一句, 看样是是字典增加键值对的操作, key: endpoint: value: func
打印下view_function
print(app.view_functions) {'static': <bound method _PackageBoundObject.send_static_file of <Flask 'flask的CBV'>>, 'login': <function View.as_view.<locals>.view at 0x000001BE77AD1E18>}
可以看到有一个字典, 里面有两个键值对, 一看就是一个字符串,对应了一个函数或变量, 这些就是url和视图的对应关系, 暂且叫他关系字典吧.
看到了里就能知道明白之前的两个疑惑
1.endpoint是做什么的?
其实就是关系字典的key, 通过endpoint就可以找到对应的视图
2.为什么视图函数不能重名?
因为没有endpoint时, 会获取视图的__name__, 就会让视图的__name__来当字典的key, 字典的key肯定是不能重名了, 所以在没有指定endpoint的情况下, 视图不能重名
回到正题, 看到这里我好想知道怎么添加对应关系了, 看下面一行代码
app.add_url_rule("/login", endpoint=None, view_func=LoginView.as_view("login"))
@app.route()调用add_url_rule()来添加对应关系, 那我不能自己调用, 肯定可以的啊.
as_view()中的参数是必须要传的, 因为会把这个当做关系字典中的key和视图对应
所以, 这就是一种给CBV添加绑定路由的方式.
CBV后面也要执行as_view()这和django一样, 甚至连内部的逻辑都是大致相同, 执行完as_view()后return出一个函数, 这个函数会获取对应请求方式的函数, 也就是执行和请求方式对应的函数
大概来看一看as_view()
@classmethod def as_view(cls, name, *class_args, **class_kwargs): def view(*args, **kwargs): self = view.view_class(*class_args, **class_kwargs) # 实例化CBV对象 return self.dispatch_request(*args, **kwargs) if cls.decorators: view.__name__ = name view.__module__ = cls.__module__ for decorator in cls.decorators: view = decorator(view) # 将参数都封装到FBV view中, 这样通过函数view的空间中就会有这些参数 view.view_class = cls view.__name__ = name view.__doc__ = cls.__doc__ view.__module__ = cls.__module__ view.methods = cls.methods view.provide_automatic_options = cls.provide_automatic_options return view # 将内部函数view返回
现在再来看看是如何添加路由的
app.add_url_rule("/login", endpoint=None, view_func=LoginView.as_view("login")) 执行完了as_view()后, 就变成下面的样子了 app.add_url_rule("/login", endpoint=None, view_func=view)
这一行代码和FBV难道不一样吗.....一模一样
这就是CBV