我们在写flask程序的时候,我们用得最多的是通过视图函数去处理每个请求。也就是说一个视图函数对应一个请求路由或者多个请求路由。比如下面的写法
from flask import Flask
app = Flask(__name__)
@app.route('/')
@app.route('/index/')
def index():
return 'Hello, welcome to Home page!'
上面的视图函数对应了两个请求url,在flask中我们一般使用这种方法来处理请求。
可插拔视图
从Flask0.7版本开始,引入了 可插拔视图,其灵感是来自于Django的通用视图。
与视图函数不同的是,可插拔视图是通过类去实现的,因此我们将它叫做为类视图。我们可以通过自定义类视图,在类中完成对应请求处理。
1. 继承自flask.views.View
类视图
加入我们需要在数据库中查询一些数据然后把它们的结果渲染到模板上,你可能会使用如下的写法
@app.route('/user/get-all/')
def index():
users = User.query.all()
return render_template('uer-list.html', users=users)
上例简单而灵活。但是如果要把这个视图变成一个可以用于其他模型和模板的通用视图, 那么这个视图还是不够灵活。因此,我们就需要引入可插拨的、基于类的视图。第一步, 可以把它转换为一个基础视图:
from flask.views import View
from flask import Flask
app = Flask(__name__)
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'))
我们创建的ShowUsers
类继承自View
类,同时在其中写了一个dispatch_request
方法。注意,如果继承自View
类,则该方法必须在在子类中实现,否则会报错。
之后我们通过as_view 方法将其转换为一个视图函数。并且通过add_url_rule
方法设置其路由规则。传递给函数的字符串就是最终视图的名称,但是这本身没有什么帮助,我们可以重构一下代码
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()
我们定义了一个继承自flask.views.View的基类,在这个基类中,我们定义了三个方法 其中 get_template_name
是子类必须实现的方法。我们在子类UserView中实现它并返回了一个模板文件,同时在子类中我们定义了获取数据的方法get_objects 用于返回数据库中查找数据的结果。
这时类视图的优雅指出就体现出来了。当我们需要获取User的数据的时候,我们自定义UserView然后继承自ListView就可以了,当我们需要获取另外一个对象的数据的时候比如Product,这时候我们定义一个ProductView继承自ListView就可以返回不同的模板以及数据了。
2. 继承自flask.views.MethodView
类视图
在互联网的世界中,我们知道存在着很多中请求方式,最常见的POST、GET等,我们可以能会经常写下面这种代码根据请求的方式不同,在视图函数中处理不同的逻辑。
@app.route('/index/', methods=['GET', 'POST'])
def index():
if request.methods == 'GET':
# do something
return 'get request response'
if request.methods == 'POST':
# do something
return 'post request response'
对于 REST 式的 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'))
在集成自MethodView的类视图中,我们不必提供 methods
属性,它会自动使用相应 的类方法。
举个栗子
from flask import views, render_template, Flask
class User(views.MethodView):
def get(self, user_id=None):
if user_id is None:
return '返回所有用户'
return '返回单个用户,用户id为{}'.format(user_id)
def post(self):
return '创建一个用户'
def delete(self, user_id):
return '删除了一个用户,用户id为{}'.format(user_id)
from flask import Flask
app = Flask(__name__)
view_func = User.as_view('user')
app.add_url_rule('/user/<int:user_id>/', view_func=view_func, methods=['GET', 'DELETE'])
app.add_url_rule('/user/', view_func=view_func, methods=['GET'])
app.add_url_rule('/user/add/', view_func=view_func, methods=['POST'])
print(app.url_map)
if __name__ == '__main__':
app.run()
在上述代码中,我们定义了一个User类视图,继承自MethodView类,在其中定义了get、post、delete三个方法。接下来我们对类视图做了url配置,运行该程序在浏览器中输入对应的url,我们可以看到结果。
输入http://127.0.0.1:5000/user/1/
总结
- 基于类的视图,能够比较优雅的方式实现很多复杂的不同功能
- 类视图定义请求方法,需要加类属性:methods = ['GET']
- 类视图用装饰器时,不能在类和类方法上直接装饰,因为我们要装饰的是视图函数(也就是最后执行的是调度类函数),View类中帮我们定义了一个增加装饰器的方法,定义类属性 decorators = [装饰器函数名]