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

 

posted @ 2018-07-25 22:04  王剑威  阅读(1552)  评论(0编辑  收藏  举报