之前我們接觸的視圖都是函數,是以一般簡稱為視圖函數。其實視圖函數也可以基于類來實作,類視圖的好處是支援繼承,寫完類視圖需要通過
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
。
![](https://img.laitimes.com/img/9ZDMuAjOiMmIsIjOiQnIsICM38FdsYkRGZkRG9lcvx2bjxiNx8VZ6l2cs0zY650dBpWWxhnMMBjVtJWd0ckW65UbM5WOHJWa5kHT20ESjBjUIF2X0hXZ0xCMx81dvRWYoNHLrdEZwZ1Rh5WNXp1bwNjW1ZUba9VZwlHdssmch1mclRXY39CXldWYtlWPzNXZj9mcw1ycz9WL49zZuBnL4QTN2MjN0kTMwIDOwkTMwIzLc52YucWbp5GZzNmLn9Gbi1yZtl2Lc9CX6MHc0RHaiojIsJye.png)
類視圖的好處:
對比類視圖和函數視圖,似乎類視圖用起來的比麻煩。其實在很多場景是有用處的,因為類可以繼承,這裡我們來舉個例子:
比如在登入頁面和注冊頁面,我們都放一個同樣的廣告,如果我們是視圖函數,就需要這樣寫:
@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
裝飾器