Flask1.0.2系列(十二) 可插拔的视图

英文原文地址:http://flask.pocoo.org/docs/1.0/views/

若有翻译错误或者不尽人意之处,请指出,谢谢~


        (新增于版本0.7。)

        Flask0.7引入了可插拔的视图,其灵感来源于Django下基于类而不是函数的通用视图。这样做的主要意图在于,你可以替换部分实现,并且这样做可以实现可定制的可插拔的视图。


1. 基础原理

        考虑以下,你有一个方法,这个方法从数据库中加载了一些对象,并且传递给模板进行渲染:

@app.route('/users/')
def show_users(page):
    users = User.query.all()
    return render_template('users.html', users=users)

        这是简单又灵活的,但是如果你想要一种通用的,同样适用于其他模块和模板的方式来提供这个视图,你可能需要更加灵活的方式才行。这就是基于类的可插拔视图能做的事。首先,把它转换为基于类的视图,你需要这样做:

from flask.views import View


class ShowUsers(View):
    
    def dispatch_request(self):
        users = User.query.all()
        return render_template('users.html', objects=users)
    

app.add_url_rule('/users/', view_func=ShowUsers.as_view('show_users'))

        正如你所看到的,你必须做的是创建了一个flask.view.View的子类,并且实现了dispatch_request()方法。然后我们必须通过使用类方法as_view(),来转换这个类到实际的视图方法。你传递给方法的字符串,是视图将会拥有的端点名称。但是这对于它本身来说没什么卵用,因此让我们稍微重构一下代码:

from flask.views import View


class ListView(View):

    def get_template_name(self):
        raise NotImplementedError()

    def render_template(self, context):
        return render_template(self.get_template_name(), **context)

    def dispatch_request(self):
        context = {'objects': self.get_objects()}
        return self.render_template(context)
    

class UserView(ListView):
    
    def get_template_name(self):
        return 'users.html'
    
    def get_objects(self):
        return User.query.all()

        当然,这对于小程序来说并没有什么卵用,但是这很好地解释了基本原理。当你有一个基于类的视图时,那么self指向的是什么呢?这种方法的工作方式是,每当有一个请求被发送,都会有一个新的类示例被创建,并使用来自URL规则的参数调用dispatch_request()函数。类本身是通过传递给as_view()方法的参数进行实例化的。举个栗子,你可以像这样书写一个类:

class RenderTemplateView(View):
    def __init__(self, template_name):
        self.template_name = template_name
    def dispatch_request(self):
        return render_template(self.template_name)

        然后你可以这样注册它:

app.add_url_rule('/about', view_func=RenderTemplateView.as_view(
    'about_page', template_name='about.html'))


2. 方法提示

        可插拔的视图就像普通的方法一样使用route()或者更好的add_url_rule()来附加到应用程序中。然而这也意味着,当你准备附加它的时候,你必须要提供视图支持的HTTP函数的名称。为了将这些信息转移到类中,你可以将这些提供一个methods属性来存储这些信息:

class MyView(View):
    methods = ['GET', 'POST']

    def dispatch_request(self):
        if request.method == 'POST':
            ...
        ...


app.add_url_rule('/myview', view_func=MyView.as_view('myview'))

3. 基于调度的方法

        对于RESTful API来说,为每个HTTP方法执行不同方法的功能特别有用。如果你需要这样做,你可以使用flask.views.MethodView。每个HTTP函数映射到一个同样名称的方法(仅仅变成了全小写):

from flask.views import MethodView


class UserAPI(MethodView):

    def get(self):
        users = User.query.all()
        ...

    def post(self):
        user = User.from_form_data(request.form)
        ...


app.add_url_rule('/users/', view_func=UserAPI.as_view('users'))
        这种方式下,你不必提供 methods属性。它会根据类中的方法定义来自动设置。


4. 修饰视图

        因为视图类本身并不是添加到路由系统的视图方法,因此修饰视图类没有什么意义。相反,你需要手动修饰的是as_view()的返回值:

def user_required(f):
    """Checks whether user is logged in or raises error 401."""
    def decorator(*args, **kwargs):
        if not g.user:
            abort(401)
        return f(*args, **kwargs)
    return decorator


view = user_required(UserAPI.as_view('users'))
app.add_url_rule('/users/', view_func=view)

        从Flask0.8开始,你还可以在类定义的时候,指定一个装饰器列表来实现这个功能:

class UserAPI(MethodView):
    decorators = [user_required]

        请记住,由于从调度者的角度来看self是不明确的,所以你不能在视图的每个函数上使用常规的视图修饰器。


5. 用于API的方法视图

        Web API通常与HTTP动词紧密结合,因此基于MethodView来实现API就变得很有意义了。也就是说,你会注意到,API大多数时候在前往相同的方法视图时,将需要不同的URL规则。例如,考虑一下你在Web上暴露一个用户对象:

URL 方法 描述
/users/ GET 获取所有用户的列表
/users/ POST 创建一个新用户
/users/<id> GET 显示单个用户
/users/<id> PUT 修改单个用户
/users/<id> DELETE 删除单个用户

        因此,你将怎么使用MethodView来实现这些功能呢?答案就是,为相同的视图提供多种规则。

        让我们假设一下视图看起来可能像这样:

class UserAPI(MethodView):

    def get(self, user_id):
        if user_id is None:
            # return a list of users
            pass
        else:
            # expose a single user
            pass

    def post(self):
        # create a new user
        pass

    def delete(self, user_id):
        # delete a single user
        pass

    def put(self, user_id):
        # update a single user
        pass

        那我们该如何将这些与路由系统联系起来呢?通过为添加两种规则,并且明确地提及每个规则:

user_view = UserAPI.as_view('user_api')
app.add_url_rule('/users/', defaults={'user_id': None},
                 view_func=user_view, methods=['GET',])
app.add_url_rule('/users/', view_func=user_view, methods=['POST',])
app.add_url_rule('/users/<int:user_id>', view_func=user_view,
                 methods=['GET', 'PUT', 'DELETE'])

        如果你有很多API与这个相似,你可以重构这些注册代码:

def register_api(view, endpoint, url, pk='id', pk_type='int'):
    view_func = view.as_view(endpoint)
    app.add_url_rule(url, defaults={pk: None},
                     view_func=view_func, methods=['GET',])
    app.add_url_rule(url, view_func=view_func, methods=['POST',])
    app.add_url_rule('%s<%s:%s>' % (url, pk_type, pk), view_func=view_func,
                     methods=['GET', 'PUT', 'DELETE'])


register_api(UserAPI, 'user_api', '/users/', pk='user_id')

猜你喜欢

转载自blog.csdn.net/regandu/article/details/80183200