New in version 0.7.
Flask 0.7 版本引入了可插撥視圖。可插撥視圖基于使用類來代替函數(shù),其靈感來自于 Django 的通用視圖??刹鍝芤晥D的主要用途是用可定制的、可插撥的視圖來替代部分實現(xiàn)。
假設有一個函數(shù)用于從數(shù)據(jù)庫中載入一個對象列表并在模板中渲染:
@app.route('/users/')
def show_users(page):
users = User.query.all()
return render_template('users.html', users=users)
上例簡單而靈活。但是如果要把這個視圖變成一個可以用于其他模型和模板的通用視圖, 那么這個視圖還是不夠靈活。因此,我們就需要引入可插撥的、基于類的視圖。第一步, 可以把它轉(zhuǎn)換為一個基礎視圖:
from flask.views import View
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'))
就如你所看到的,必須做的是創(chuàng)建一個 flask.views.View 的子類,并且執(zhí)行 dispatch_request() 。然后必須通過使用 as_view() 方法把類轉(zhuǎn)換為實際視圖函數(shù)。傳遞給函數(shù)的 字符串是最終視圖的名稱。但是這本身沒有什么幫助,所以讓我們來小小地重構一下:
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()
這樣做對于示例中的小應用沒有什么用途,但是可以足夠清楚的解釋基本原理。當你有一個基礎視圖類時,問題就來了:類的 self 指向什么?解決之道是:每當請求發(fā)出時就創(chuàng)建一個類的新實例,并且根據(jù)來自 URL 規(guī)則的參數(shù)調(diào)用 dispatch_request() 方法。類本身根據(jù)參數(shù)實例化后傳遞給 as_view() 函數(shù)。例如可以這樣寫一個類:
class RenderTemplateView(View):
def __init__(self, template_name):
self.template_name = template_name
def dispatch_request(self):
return render_template(self.template_name)
然后可以這樣注冊:
app.add_url_rule('/about', view_func=RenderTemplateView.as_view(
'about_page', template_name='about.html'))
可插撥視圖可以像普通函數(shù)一樣加入應用。加入的方式有兩種,一種是使用 route() ,另一種是使用更好的 add_url_rule() 。在加入的視圖中應該提供所使用的 HTTP 方法的名稱。提供名稱的方法是使用 methods 屬性:
class MyView(View):
methods = ['GET', 'POST']
def dispatch_request(self):
if request.method == 'POST':
...
...
app.add_url_rule('/myview', view_func=MyView.as_view('myview'))
對于 REST 式的 API 來說,為每種 HTTP 方法提供相對應的不同函數(shù)顯得尤為有用。使用 flask.views.MethodView 可以輕易做到這點。在這個類中,每個 HTTP 方法 都映射到一個同名函數(shù)(函數(shù)名稱為小寫字母):
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'))
使用這種方式,不必提供 methods 屬性,它會自動使用相應的類方法。
視圖函數(shù)會被添加到路由系統(tǒng)中,而視圖類則不會。因此視圖類不需要裝飾,只能以手工 使用 as_view() 來裝飾返回值:
def user_required(f):
"""Checks whether user is logged in or raises error 401."""
def decorator(*args, **kwargs):
if not g.user:
abort(401)
return f(*args, **kwargs)
return decorator
view = user_required(UserAPI.as_view('users'))
app.add_url_rule('/users/', view_func=view)
自 Flask 0.8 版本開始,新加了一種選擇:在視圖類中定義裝飾的列表:
class UserAPI(MethodView):
decorators = [user_required]
請牢記:因為從調(diào)用者的角度來看,類的 self 被隱藏了,所以不能在類的方法上單獨使用裝飾器。
網(wǎng)絡 API 經(jīng)常直接對應 HTTP 變量,因此很有必要實現(xiàn)基于 MethodView 的 API 。即多數(shù)時候, API 需要把不同的 URL 規(guī)則應用到同一個方法視圖。例如,假設你需要這樣使用一個 user 對象:
| URL | 方法 | 說明 |
|---|---|---|
| /users/ | GET | 給出一個包含所有用戶的列表 |
| /users/ | POST | 創(chuàng)建一個新用戶 |
| /users/ |
GET | 顯示一個用戶 |
| /users/ |
PUT | 更新一個用戶 |
| /users/ |
DELETE | 刪除一個用戶 |
那么如何使用 MethodView 來實現(xiàn)呢?方法是使用多個規(guī)則對應 到同一個視圖。
假設視圖是這樣的:
class UserAPI(MethodView):
def get(self, user_id):
if user_id is None:
# 返回一個包含所有用戶的列表
pass
else:
# 顯示一個用戶
pass
def post(self):
# 創(chuàng)建一個新用戶
pass
def delete(self, user_id):
# 刪除一個用戶
pass
def put(self, user_id):
# update a single user
pass
那么如何把這個視圖掛接到路由系統(tǒng)呢?方法是增加兩個規(guī)則并為每個規(guī)則顯式聲明方法:
user_view = UserAPI.as_view('user_api')
app.add_url_rule('/users/', defaults={'user_id': None},
view_func=user_view, methods=['GET',])
app.add_url_rule('/users/', view_func=user_view, methods=['POST',])
app.add_url_rule('/users/<int:user_id>', view_func=user_view,
methods=['GET', 'PUT', 'DELETE'])
如果你有許多類似的 API ,那么可以代碼如下:
def register_api(view, endpoint, url, pk='id', pk_type='int'):
view_func = view.as_view(endpoint)
app.add_url_rule(url, defaults={pk: None},
view_func=view_func, methods=['GET',])
app.add_url_rule(url, view_func=view_func, methods=['POST',])
app.add_url_rule('%s<%s:%s>' % (url, pk_type, pk), view_func=view_func,
methods=['GET', 'PUT', 'DELETE'])
register_api(UserAPI, 'user_api', '/users/', pk='user_id')
? Copyright 2013, Armin Ronacher. Created using Sphinx.