之前我们接触的视图都是函数,所以一般简称为视图函数。其实视图函数也可以基于类来实现,类视图的好处是支持继承,写完类视图需要通过app.add_url_rule(url_rule, view_func)
来进行注册。
标准类视图
1、标准类视图必须继承flask.views.View
2、必须实现dispatch_request方法,以后请求过来后,都会执行这个方法,这个方法的返回值相当于之前的视图函数一样,也必须返回Response或者子类的对象,或者是字符串、元祖
3、必须通过app.add_url_rule(rule, endpoint, view_func)来做url与视图的映射,view_func这个参数,需要使用as_view类方法转换
4、如果指定了endpoint,那么在使用url_for反转的时候就必须使用endpoint指定的那个值。如果没有指定endpoint,那么就可以使用as_view(视图名字)中指定的视图名字来作为反转。
...
from flask import views
class ListView(views.View):
def dispatch_request(self):
return 'my list'
app.add_url_rule(rule='/list/', endpoint='list', view_func=ListView.as_view('list'))
view_func=ListView.as_view('list')
为什么要这样用呢,这是因为view_func
是接收函数的,而ListView
是一个类,通过as_view
方法转换出来的就是一个函数,而这个函数实际上就是dispatch_request
, 里面的‘list
’是给这个视图函数取的一个名字。 我们可以按住ctrl点击as_view
查看源码,就知道返回的是dispatch_request
。
类视图的好处:
对比类视图和函数视图,似乎类视图用起来的比麻烦。其实在很多场景是有用处的,因为类可以继承,这里我们来举个例子:
比如在登录页面和注册页面,我们都放一个同样的广告,如果我们是视图函数,就需要这样写:
@app.route('/login/')
def login():
context = {
'ads': 'python入门到精通'
}
return render_template('login.html', **context)
@app.route('/register/')
def register():
context = {
'ads': 'Python入门到精通'
}
return render_template('register.html', **context)
使用这种方法明显存在一个缺点:就是当广告需要更换的时候,两个视图函数都需要更改,如果我们使用类视图,就可以使用继承来解决,届时只需要修改父类中的参数即可。
class AdsView(views.View):
def __init__(self):
super().__init__()
self.context = {
'ads': '今年过节不收礼'
}
class LoginView(AdsView):
def dispatch_request(self):
return render_template('login.html', **self.context)
class RegisterView(AdsView):
def dispatch_request(self):
return render_template('regist.html', **self.context)
app.add_url_rule(rule='/login/', endpoint='login', view_func=LoginView.as_view('login'))
app.add_url_rule(rule='/regist/', endpoint='regist', view_func=RegisterView.as_view('regist'))
基于调度方法的类视图
Flask还为我们提供了另外一种类视图flask.views.MethodView
,对每个HTTP方法执行不同的函数(映射到对应方法的小写的同名方法上),
我们来模拟做一个登录页面, 一般登录我们会用到两种方法,当进入登录页面的时候我们会用get方法,当我们填好表单点击登录的时候会用到post方法
class LoginView(views.MethodView):
def get(self):
return render_template('login.html')
def post(self):
username = request.form.get('username')
password = request.form.get('password')
if username == 'heboan' and password == '123456':
return '登录成功'
else:
return render_template('login.html', error='登录失败')
app.add_url_rule(rule='/login/', endpoint='login', view_func=LoginView.as_view('login'))
login.html
<form action="" method="post">
<table>
<tr>
<td>用户名:</td>
<td><input name="username" type="text" /></td>
</tr>
<tr>
<td>密码:</td>
<td><input name="password" type="password" /></td>
</tr>
<tr>
<td><input type="submit" value="登录"></td>
</tr>
</table>
{% if error %}
<p style="color: red">{{ error }}</p>
{% endif %}
</form>
其实还可以对这段代码进行优化。我们发现我们一开始进入登录页面,和登录失败都是返回login.html。
class LoginView(views.MethodView):
def __render(self, error=None): # 双下划线开头的函数是类的私有方法。
return render_template('login.html', error=error)
def get(self):
return self.__render()
def post(self):
username = request.form.get('username')
password = request.form.get('password')
if username == 'heboan' and password == '123456':
return '登录成功'
else:
return self.__render(error='用户名或密码失败!')
app.add_url_rule(rule='/login/', view_func=LoginView.as_view('login'))
类视图中使用装饰器
在实际开发中,我们有时候会用到自己定义装饰器并应用到函数视图或者类视图里面:
比如:我们要想进入个人中心页面,首先要验证你是否登录,否则进不去,下面我们来模拟这个场景
定义一个装饰器
from functools import wraps
...
def login_required(func):
@wraps(func) #保持原来函数的特性
def wrapper(*args, **kwargs ):
# /setting/?username=heboan
username = request.args.get('username')
if username and username == 'heboan':
return func(*args, **kwargs)
else:
return '请先登录'
return wrapper
函数视图应用自定义装饰器
@app.route('/lprofile/')
@login_required #这个必须放在@app.route装饰器下面
def profile():
return render_template('profile.html')
类视图应用自定义的装饰器
class ProfileView(views.View):
decorators = [login_required]
def dispatch_request(self):
return render_template('profile.html')
app.add_url_rule('/profile/', view_func=ProfileView.as_view('profile'))
这就相当于对dispatch_request()
这个函数加了上面自定义的一个login_required
装饰器