以往使用的视图都是函数,一般称为视图函数。在Flask框架中视图还可以使用类来实现,类视图的好处是支持继承,使用类视图还是和函数视图有区别的,需要写完类视图后再通过app.add_url_rule(url_rule, view_func)来进行注册。
1、标准类视图
1.1 标准类视图使用步骤
标准类视图是继承自flask.views.View,并且在子类中必须实现dispatch_request方法,这个方法类似于视图函数,这个方法的返回值就相当于是之前的视图函数一样。也要返回一个基于Response或者其子类的对象,或者是字符串,或者是元组。
必须通过 app.add_url_rule(rule,endpoint,view_func) 来做url与视图的映射。view_func 这个参数,需要使用类视图下的 as_view 类方法类转换:ListView.as_view('list') 。
如果指定了 endpoint ,那么在使用 url_for 反转的时候就必须使用endpoint 指定的那个值。如果没有指定 endpoint ,那么就可以使用as_view(视图名字) 中指定的视图名字来作为反转。
示例代码1:
from flask import Flask, render_template
from flask.views import View
app = Flask(__name__)
@app.route('/')
def index():
return render_template('index.html')
class PersonView(View):
def dispatch_request(self):
return '我是标准类视图'
# 类视图通过add_url_rule方法和url做映射, 注意:user后面的斜杠不能去掉
app.add_url_rule('/user/', view_func=PersonView.as_view('personalview'))
if __name__ == '__main__':
app.run()
运行效果:
示例代码2:
from flask import Flask, url_for
from flask.views import View
app = Flask(__name__)
@app.route('/')
def index():
print(url_for('my_test'))
# print(url_for('my_test2')) # 错误写法
print(url_for('test2'))
return "hello World!"
class TestView(View):
def dispatch_request(self):
return '返回一个TestView测试内容!'
class TestView2(View):
def dispatch_request(self):
return '返回一个TestView2测试内容!'
app.add_url_rule('/test', view_func=TestView.as_view('my_test'))
app.add_url_rule('/test2', view_func=TestView2.as_view('my_test2'), endpoint='test2')
# 用于测试
with app.test_request_context():
print(url_for('my_test'))
print(url_for('test2'))
if __name__ == '__main__':
app.run(debug=True)
运行结果:
1.2 类视图的好处
1. 可以继承,把一些共性的东西抽取出来放到父视图中,子视图直接拿来用就可以了。
2. 但是也不是说所有的视图都要使用类视图,这个要根据情况而定。视图函数用得最多。
示例代码:
from flask import Flask, jsonify
from flask.views import View
app = Flask(__name__)
# 需求:返回的结果都必须是json数据
class BaseView(View):
def get_data(self):
raise NotImplementedError
def dispatch_request(self):
return jsonify(self.get_data())
class JsonView(BaseView):
def get_data(self):
return {'name': 'dgw', 'age': 26}
class JsonView2(BaseView):
def get_data(self):
return [{'name': 'dgw', 'age': 26}, {'name': 'dgw2', 'age': 27}]
app.add_url_rule('/base', view_func=BaseView.as_view('base'))
app.add_url_rule('/json', view_func=JsonView.as_view('json'), endpoint='json')
app.add_url_rule('/json2', view_func=JsonView2.as_view('json2'), endpoint='json2')
if __name__ == '__main__':
app.run(debug=True)
运行结果:
2、基于调度方法的类视图
2.1 调度方法类视图的使用
Flask框架还提供了另外一种类视图flask.views.MethodView,对每个HTTP方法执行不同的函数(映射到对应方法的小写的同名方法上)。
1. 基于方法的类视图,是根据请求的 method 来执行不同的方法的。如果用户是发送的 get 请求,那么将会执行这个类的 get 方法。如果用户发送的是 post 请求,那么将会执行这个类的 post 方法。其他的method类似,比如 delete 、 put
2. 这种方式,可以让代码更加简洁。所有和 get 请求相关的代码都放在 get 方法中,所有和 post 请求相关的代码都放在 post 方法中。就不需要跟之前的函数一样,通过 request.method == 'GET'。
示例代码1:
main.py
from flask import Flask, render_template, request
from flask.views import MethodView
app = Flask(__name__)
# 定义一个基于方法调度的 类视图
class LoginView(MethodView):
def get(self, msg=None):
return render_template('login.html', msg=msg)
def post(self):
# 模拟实现
# 拿到前端页面传过来的 账号 和密码 去数据库做查询操作 查询到 (跳转主页面) ,反之跳转到login.html页面并给出错误提示信息
uname = request.form['uname']
pwd = request.form['pwd']
if uname == 'dgw' and pwd == '123':
return render_template('index.html')
else:
return render_template('login.html', msg='用户名或者密码错误!')
# 注册类视图
app.add_url_rule('/login/', view_func=LoginView.as_view('login'))
if __name__ == '__main__':
app.run(debug=True)
index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>首页</title>
</head>
<body>
<h2>欢迎登录首页!</h2>
</body>
</html>
login.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>登录页面</title>
</head>
<body>
<h2>Login</h2>
<form action="/login/" method="POST">
<table>
<tr>
<td>用户名:</td>
<td><input type="text" name='uname'></td>
</tr>
<tr>
<td>密码:</td>
<td><input type="password" name='pwd'></td>
</tr>
<tr>
<td><input type="submit" value="登录"></td>
</tr>
</table>
</form>
{% if msg %}
<span style="color: red;">{{ msg }}</span>
{% endif %}
</body>
</html>
运行结果:
示例代码2: 【在示例代码1的基础上进行改进】
基于调度方法的类视图, 通常get()方法处理get请求,post()方法处理post请求.
为了便于管理,不推荐post方法和get方法互相调用.
main.py
from flask import Flask, render_template, request
from flask.views import MethodView
app = Flask(__name__)
# 定义一个基于方法调度的 类视图
class LoginView(MethodView):
def __jump(self, msg=None):
return render_template('login.html', msg=msg)
def get(self, msg=None):
return self.__jump(msg)
def post(self):
# 模拟实现
# 拿到前端页面传过来的 账号 和密码 去数据库做查询操作 查询到 (跳转主页面) ,反之跳转到login.html页面并给出错误提示信息
uname = request.form['uname']
pwd = request.form['pwd']
if uname == 'dgw' and pwd == '123':
return render_template('index.html')
else:
return self.__jump(msg='用户名或者密码错误!')
# 注册类视图
app.add_url_rule('/login/', view_func=LoginView.as_view('login'))
if __name__ == '__main__':
app.run(debug=True)
示例代码3:
from flask import Flask, render_template, request
from flask.views import MethodView
app = Flask(__name__)
@app.route('/')
def index():
return render_template('index.html')
class LoginView(MethodView):
# 当客户端页面请求访问时执行get函数
def get(self):
return render_template('login.html')
# 当客户端向页面发送数据post请求时执行post函数
def post(self):
name = request.form.get('name')
password = request.form.get('password')
if name == 'dgw' and password == '666':
return '登录成功!'
else:
return '用户名或密码错误!'
# 类视图通过add_url_rule方法和url做映射, 并且在as_view方法中指定url的名称,方便url_for函数的调用
app.add_url_rule('/login/', view_func=LoginView.as_view('loginview'))
if __name__ == '__main__':
app.run()
当方式get请求时,运行效果:
当方式post请求时,运行效果: 【使用postman工具发起post请求】
2.2 装饰器的使用
简言之,python装饰器就是用于拓展原来函数功能的一种函数,这个函数的特殊之处在于它的返回值也是一个函数。
使用python装饰器的好处就是在不用更改原函数的代码前提下给函数增加新的功能。
当使用类视图时,也可以使用装饰器。如在做权限验证的时候。
装饰器功能写完后,在类视图中定义一个属性叫做decorators,然后存储装饰器。以后每次调用这个类视图的时候,就会执行这个装饰器。
在视图函数中使用自定义装饰器,那么自己定义的装饰器必须放在 app.route 下面。否则这个装饰器就起不到任何作用。
在类视图中使用装饰器,需要重写类视图的一个类属性 decorators ,这个类属性是一个列表或者元组都可以,里面装的就是所有的装饰器。
示例代码1:
from flask import Flask, render_template, request
from flask.views import MethodView
from functools import wraps
import logging
# 设置日志模块
logging.basicConfig(level=logging.INFO)
app = Flask(__name__)
def login_required(func):
@wraps(func)
def wrapper(*args, **kwargs):
uname = request.args.get('uname')
pwd = request.args.get('pwd')
if uname == 'dgw' and pwd == '123':
logging.info(f'{uname}:登录成功!!')
return func(*args, **kwargs)
else:
logging.info(f'{uname}:尝试登录失败!!')
return '用户名或密码错误!!'
return wrapper
def logger(func):
@wraps(func)
def wrapper(*args, **kwargs):
logging.info('这个测试日志信息!!!')
return func(*args, **kwargs)
return wrapper
@app.route('/')
@login_required
def index():
return 'Hello'
class LoginView(MethodView):
decorators = [login_required, logger]
def get(self):
return 'Hello'
app.add_url_rule('/login/', view_func=LoginView.as_view('login'))
if __name__ == '__main__':
app.run(debug=True)
运行结果:
示例代码2:
check_auth.py
from flask import session
def login_required(func):
def wrapper(*args, **kwargs):
if not session.get("user_id"):
return 'auth failure'
return func(*args, **kwargs)
return wrapper
main.py
from flask import Flask, render_template, request
from flask.views import MethodView
from check_auth import login_required
app = Flask(__name__)
@app.route('/')
def index():
return render_template('index.html')
class LoginView(MethodView):
decorators = [login_required]
# 当客户端页面请求访问时执行get函数
def get(self):
return render_template('login.html')
# 当客户端向页面发送数据post请求时执行post函数
def post(self):
name = request.form.get('name')
password = request.form.get('password')
if name == 'dgw' and password == '666':
return '登录成功!'
else:
return '用户名或密码错误!'
# 类视图通过add_url_rule方法和url做映射, 并且在as_view方法中指定url的名称,方便url_for函数的调用
app.add_url_rule('/login/', view_func=LoginView.as_view('loginview'))
if __name__ == '__main__':
app.run()
运行效果: