Flask 标准类视图
在前面的小节讲解 Flask 路由时,采用的方式是:将 URL 路径和一个函数关联,当 Flask 框架接收到请求后,会根据请求的 URL 调用相应的函数进行处理。
本小节讲解设置路由的新方法:将 URL 路径和一个视图类关联,当 Flask 框架接收到请求后,会根据请求的 URL 调用相应的视图类进行处理。
1. 视图函数与视图类
在前面小节中,将 URL 路径和一个函数关联,这个函数又被称为视图函数,Flask 框架会根据请求的 URL 调用相应的视图函数进行处理,例如:
from flask import Flask
app = Flask(__name__)
@app.route('/')
def index():
return 'hello world'
app.run(debug = True)
在这个例子中,URL 是 /,对应的处理函数是 index,当访问路径为 / 时,会执行函数 index。
在前面的小节中,使用函数处理相关的 URL,所以一般简称为视图函数。在 Flask 中,也可以使用类来处理相关的 URL,这样的也被称为视图类。
2. 基本用法
2.1 基本用法
Flask.views.View 是 Flask 的标准视图类,用户定义的视图类需要继承于 Flask.views.View。使用视图类的步骤如下:
- 用户定义一个视图类,继承于 flask.views.View;
- 在视图类中定义方法 dispatch_request,处理请求、返回 HTML 文本给客户端;
- 使用 app.add_url_rule (rule, view_func) 将 URL 路径和视图类绑定
下面是视图类的例子 basis.py
:
from flask import Flask, views
app = Flask(__name__)
class Index(views.View) :
def dispatch_request(self):
return 'hello world'
app.add_url_rule(rule='/', view_func = Index.as_view('Index'))
app.run(debug = True)
在第 4 行,定义视图类 Index 用于处理路径为 / 的 URL,视图类 Index 继承于 Flask.views.View;在第 5 行,定义方法 dispatch_request,该方法返回 ‘hello world’ 给客户端;在第 8 行,将路径为 / 的 URL 和视图类 Index 绑定。
Tips:Index.as_view (‘Index’) 创建一个名称为 Index 的视图函数,app.add_url_rule 实际上是将 URL 路径和视图函数(由视图类的 as_view 转换而来)绑定。
2.2 as_view 函数的功能
视图类的本质是视图函数,函数 View.as_view () 返回一个视图函数,为了透彻理解 as_view 函数的功能,我们自己实现一个简化版本的 as_view 函数,创建文件 as-view.py 如下:
from flask import Flask
app = Flask(__name__)
class Index:
def dispatch_request(self):
return 'hello world'
@staticmethod
def as_view(name):
index = Index()
return index.dispatch_request
app.add_url_rule(rule='/', view_func = Index.as_view('Index'))
app.run(debug = True)
这个小节的程序功能与上一个小节的程序功能完全相同,不同之处:在本小节的程序中,我们实现了一个简化版本的 as_view 函数。
在第 8 行,定义了一个静态方法 as_view,它首先创建一个实例 index,然后返回实例 index 的 dispatch_request 方法。即 view_func 指向了实例 index 的方法 dispatch_request,当访问页面路径 / 时,最终会调用 index.dispatch_request ()。
3. 继承
使用类视图的好处是支持继承,可以把一些共性的东西放在父类中,其他子类可以继承。下面通过一个例子 inherit.py 说明如何使用继承。
3.1 父类 BaseView
首先,定义视图类 BaseView,它继承于 flask.views.View,它是用户自定义的视图类的父类,代码如下:
from flask import Flask, render_template, views
app = Flask(__name__)
class BaseView(views.View):
def get_template(self):
raise NotImplementedError()
def get_data(self):
raise NotImplementedError()
def dispatch_request(self):
data = self.get_data()
template = self.get_template()
return render_template(template, **data)
BaseView 与子类之间的关系如下:BaseView 提供了 dispatch_request 方法的实现,子类继承了这个方法,不需要重新实现 dispatch_request;BaseView 定义了两个接口函数 get_template 和 get_data,子类必须实现这两个方法。
在 BaseView 中,get_template 和 get_data 的缺省实现是抛出错误 NotImplementedError,如果子类忘记定义了这两个方法,在运行时会报错。
在 BaseView 中,定义了方法 displach_request (),调用 get_template () 获得模板的路径,调用 get_data () 获取模板的参数,最后调用 render_template 根据模板路径 template 和模板参数 data 渲染输出。
3.2 子类 UserView
实现子类 UserView,它继承于 BaseView,代码如下:
class UserView(BaseView):
def get_template(self):
return 'user.html'
def get_data(self):
return {
'name': 'zhangsan',
'gender': 'male',
}
在子类 UserView 中,get_template 返回模板路径为 ‘user.html’,get_data 返回模板参数 name 和 gender。BaseView 中已经实现了视图类的 dispatch_request 方法,子类 UserView 继承了 BaseView 的 dispatch_request 方法,因此不需要再重新实现该方法。
3.3 绑定 URL
使用 app.add_url_rule 绑定 URL 和视图类,如下:
app.add_url_rule('/user/', view_func=UserView.as_view('UserView'))
app.run(debug = True)
将路径为 /user/ 的 URL 和视图类 UserView 绑定,当访问路径为 /user/ 的 URL 时,最终由 BaseView.dispatch_request 进行处理。
3.4 模板 user.html
创建模板 user.html:
<html>
<body>
<h2>name = {{ name }}</h2>
<h2>gender = {{ gender }}</h2>
</body>
</html>
3.5 运行结果
在浏览器中访问 http://localhost:5000/user
,显示如下:
4. 使用装饰器
本小节通过具体的例子讲解如何在视图函数和视图类中使用装饰器。
4.1 检查登录的功能
在 Web 应用中,相当一部分功能需要用户登录才能使用,比如说:访问用户的个人信息页面,在访问页面之前,需要检查用户是否已经成功登录,只有成功登录后才执行后续的功能逻辑。我们可以这样实现:
def check_login():
if 用户已经登录:
return True
else:
return False
@app.route('/page1', page1)
def page1():
if not check_login():
return '请先登录'
执行 page1 的功能
@app.route('/page2', page2)
def page2():
if not check_login():
return '请先登录'
执行 page2 的功能
在上面例子中,处理 /page1 和 /page2 时需要检查登录,在函数 page1 () 和 page2 () 的头部调用 check_login 函数。这种方法虽然实现了功能,但不够简洁。
4.2 检查登录的装饰器
使用装饰器实现登录的功能,定义检查登录的装饰器 check_login:
from functools import wraps
def check_login(original_function):
@wraps(original_function)
def decorated_function(*args,**kwargs):
user = request.args.get("user")
if user and user == 'zhangsan':
return original_function(*args, **kwargs)
else:
return '请先登录'
return decorated_function
装饰器 check_login 本质是一个函数,它的输入是一个函数 original_function,它的输出也是一个函数 decorated_function。original_function 是原先的处理 URL 的视图函数,它不包含检查登录的功能逻辑;decorated_function 是在 original_function 的基础上进行功能扩充的函数,它首先检查是否已经登录,如果已经登录则调用 original_function,如果没有登录则返回错误。
在第 6 行和第 7 行,检查请求中的参数 user 是否为 ‘zhangsan’,如果 user 等于 ‘zhangsan’,表示用户已经登录,则调用 original_function,否则返回 ‘请先登录’。
在第 4 行,使用 functools.wraps (original_function) 保留原始函数 original_function 的属性。
4.3 在视图函数中使用装饰器
本小节讲解在视图函数中使用装饰器,创建文件 decorator/view-func.py:
from flask import Flask, request
from functools import wraps
app = Flask(__name__)
def check_login(original_function):
# 请参考 4.2 小节
@app.route('/page1')
@check_login
def page1():
return 'page1'
@app.route('/page2')
@check_login
def page2():
return 'page2'
app.run(debug = True)
程序有 2 个页面 /page1 和 /page2,只有登录的用户才能访问这两个页面。
函数 page1 被装饰了 2 次,它的原始功能是处理 /page1 的页面逻辑,被 @check_login 装饰后,具备了检查登录的功能,被 @app.route (’/page1’) 装饰后,绑定到路径为 /page1 的 URL,当访问 /page1 时,会访问 page1 函数。函数 page2 的功能类似。
与 4.1 小节相比,使用装饰器后,检查登录的功能与 page1 和 page2 本身的功能是分离的。
4.4 在视图类中使用装饰器
本小节讲解在视图类中使用装饰器,创建文件 decorator/view-class.py:
:
from flask import Flask, request, views
from functools import wraps
app = Flask(__name__)
def check_login(func):
# 请参考 4.2 小节
class Page1(views.View):
decorators = [check_login]
def dispatch_request(self):
return 'Page1'
class Page2(views.View):
decorators = [check_login]
def dispatch_request(self):
return 'Page2'
app.add_url_rule(rule='/page1', view_func = Page1.as_view('Page1'))
app.add_url_rule(rule='/page2', view_func = Page2.as_view('Page2'))
app.run(debug = True)
程序有 2 个页面 /page1 和 /page2,只有登录的用户才能访问这两个页面。
类 Page1 的原始功能是处理 /page1 的页面逻辑,在第 9 行,decorators = [check_login] 设定视图类的装饰器,当访问 /page1 时,首先执行检查登录,然后再执行原始的功能。
与 4.1 小节相比,使用装饰器后,检查登录的功能与 page1 和 page2 本身的功能是分离的。
4.5 运行效果
在浏览器中输入:http://localhost:5000/page1?user=zhangsan, URL 中的查询参数 user=zhangsan 表示已经登录,显示如下:
5. 源代码下载
6. 小结
本小节讲解设置路由的新方法:将 URL 路径和一个视图类关联,当 Flask 框架接收到请求后,会根据请求的 URL 调用相应的视图类进行处理。对本节的重点,使用思维导图概括如下: