Flask 学习-22.可插拨视图MethodView类
前言
可插拨视图基于使用类来代替函数,其灵感来自于 Django 的通用视图。可插拨视图的主要用途是用可定制的、可插拨的视图来替代部分 实现。
基本原理
假设有一个函数用于从数据库中载入一个对象列表并在模板中渲染:
@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.views.View 的子类,并且执行 dispatch_request() 。然后必须通过使用 as_view() 方法把类转换为实际视图函数。
插拨视图可以像普通函数一样加入应用。加入的方式有两种,一种是使用 route() ,另一种是使用更好的 add_url_rule() 。在加入的视图中应该提供所使用的 HTTP 方法的 名称。提供名称的方法是使用 methods 属性:
from flask.views import View
from flask import request
class MyView(View):
# 定义请求方法
methods = ["GET", "POST"]
# get请求
def get(self):
return "get"
# post请求
def post(self):
return "post"
# 调度函数,必须重写。不重写,View类会直接抛异常
def dispatch_request(self):
# 请求方法映射关系
request_method = {"get": self.get, "post": self.post}
# 通过requset方法获取前端访问的请求方法
print(request.method)
# 通过请求方法,映射到对应的函数对象
view = request_method.get(request.method.lower())
# 返回映射到的函数返回值
return view()
# 注册,用as_view方法
app.add_url_rule("/my", view_func=MyView.as_view("myview"))
看到这可能已经蒙圈,前面是讲基本实现原理,dispatch_request()方法必须要自己重写,并且不好理解怎么去写它,接下来讲简单的实现方式,让大家轻轻松松学会。
MethodView 基于方法调度
MethodView是 前面View 的升级版本,不需要我们写dispatch_request()方法了,接下来看下面更优雅的实现方式
from flask.views import View, MethodView
class MyView(MethodView):
def get(self):
return {"code": 0, "msg": "get 请求"}
def post(self):
return {"code": 0, "msg": "post 请求"}
# 注册,用as_view方法
app.add_url_rule("/my", view_func=MyView.as_view("myview"))
使用这种方式,不必提供 methods 属性,它会自动使用相应 的类方法。
于是发get请求,得到结果
GET http://127.0.0.1:5000/my
{
"code": 0,
"msg": "get 请求"
}
发post请求,得到结果
POST http://127.0.0.1:5000/my
{
"code": 0,
"msg": "post 请求"
}
实现restful API风格接口
网络 API 经常直接对应 HTTP 变量,因此很有必要实现基于 MethodView 的 API 。即多数时候, API 需要把不同的 URL 规则应用到同一个方法视图。例如,假设你需要这样使用一个 user 对象:
URL | 方法 | 说明 |
---|---|---|
/users/ | GET | 给出一个包含所有用户的列表 |
/users/ | POST | 创建一个新用户 |
/users/ |
GET | 显示一个用户 |
/users/ |
PUT | 更新一个用户 |
/users/ |
DELETE | 删除一个用户 |
那么如何使用 MethodView 来实现呢?方法是使用多个规则对应 到同一个视图。
假设视图是这样的:
class UserAPI(MethodView):
def get(self, user_id):
if user_id is None:
# 返回一个包含所有用户的列表
pass
else:
# 显示一个用户
pass
def post(self):
# 创建一个新用户
pass
def delete(self, user_id):
# 删除一个用户
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')
装饰视图
视图函数会被添加到路由系统中,而视图类则不会。因此视图类不需要装饰,只能以手工 使用 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)
自 Flask 0.8 版本开始,新加了一种选择:在视图类中定义装饰的列表:
class UserAPI(MethodView):
decorators = [user_required]
请牢记:因为从调用者的角度来看,类的 self 被隐藏了,所以不能在类的方法上单独 使用装饰器。