視圖裝飾器¶
Python 有一個非常有趣的功能叫做函數裝飾器。這為 Web 應用程式帶來了一些非常棒的功能。因為 Flask 中的每個視圖都是一個函數,所以裝飾器可以用來將額外的功能注入到一個或多個函數中。您可能已經使用過 route()
裝飾器。但是,在某些情況下,您需要實作自己的裝飾器。例如,假設您有一個視圖應該只供已登入的使用者使用。如果使用者訪問該網站但未登入,則應將其重新導向到登入頁面。這是一個很好的用例範例,裝飾器是一個絕佳的解決方案。
需要登入裝飾器¶
那麼,讓我們實作這樣一個裝飾器。裝飾器是一個包裝和取代另一個函數的函數。由於原始函數被取代了,您需要記住將原始函數的資訊複製到新函數中。使用 functools.wraps()
來為您處理這個問題。
此範例假設登入頁面稱為 'login'
,並且目前使用者儲存在 g.user
中,如果沒有人登入,則為 None
。
from functools import wraps
from flask import g, request, redirect, url_for
def login_required(f):
@wraps(f)
def decorated_function(*args, **kwargs):
if g.user is None:
return redirect(url_for('login', next=request.url))
return f(*args, **kwargs)
return decorated_function
要使用裝飾器,請將其作為最內層的裝飾器應用於視圖函數。當應用更多裝飾器時,請始終記住 route()
裝飾器是最外層的。
@app.route('/secret_page')
@login_required
def secret_page():
pass
注意
next
值將在登入頁面的 GET
請求之後存在於 request.args
中。您必須在從登入表單發送 POST
請求時將其傳遞下去。您可以使用隱藏的輸入標籤來執行此操作,然後在使用者登入時從 request.form
中檢索它。
<input type="hidden" value="{{ request.args.get('next', '') }}"/>
快取裝飾器¶
假設您有一個視圖函數執行了昂貴的計算,因此您想快取產生的結果一段時間。裝飾器對於此目的會很好用。我們假設您已設定了如 快取 中所述的快取。
這是一個快取函數的範例。它從特定的前綴(實際上是一個格式字串)和請求的當前路徑生成快取鍵。請注意,我們正在使用一個首先建立裝飾器,然後裝飾函數的函數。聽起來很糟糕嗎?不幸的是,它有點複雜,但是程式碼仍然應該很容易閱讀。
裝飾後的函數將按如下方式工作
根據當前路徑取得當前請求的唯一快取鍵。
從快取中取得該鍵的值。如果快取返回了某些內容,我們將返回該值。
否則,將呼叫原始函數,並且傳回值將儲存在快取中,超時時間為提供的時間(預設為 5 分鐘)。
這是程式碼
from functools import wraps
from flask import request
def cached(timeout=5 * 60, key='view/{}'):
def decorator(f):
@wraps(f)
def decorated_function(*args, **kwargs):
cache_key = key.format(request.path)
rv = cache.get(cache_key)
if rv is not None:
return rv
rv = f(*args, **kwargs)
cache.set(cache_key, rv, timeout=timeout)
return rv
return decorated_function
return decorator
請注意,這假設已實例化 cache
物件可用,請參閱 快取。
範本裝飾器¶
TurboGears 的人員在不久前發明了一種常見模式,即範本裝飾器。該裝飾器的想法是,您從視圖函數返回一個包含傳遞給範本的值的字典,並且範本會自動呈現。這樣,以下三個範例的功能完全相同
@app.route('/')
def index():
return render_template('index.html', value=42)
@app.route('/')
@templated('index.html')
def index():
return dict(value=42)
@app.route('/')
@templated()
def index():
return dict(value=42)
如您所見,如果未提供範本名稱,它將使用 URL 對應的端點,並將點轉換為斜線 + '.html'
。否則,將使用提供的範本名稱。當裝飾後的函數返回時,傳回的字典將傳遞給範本呈現函數。如果返回 None
,則假定為空字典;如果返回字典以外的其他內容,我們將從函數中傳回未更改的內容。這樣,您仍然可以使用重新導向函數或傳回簡單的字串。
這是該裝飾器的程式碼
from functools import wraps
from flask import request, render_template
def templated(template=None):
def decorator(f):
@wraps(f)
def decorated_function(*args, **kwargs):
template_name = template
if template_name is None:
template_name = f"{request.endpoint.replace('.', '/')}.html"
ctx = f(*args, **kwargs)
if ctx is None:
ctx = {}
elif not isinstance(ctx, dict):
return ctx
return render_template(template_name, **ctx)
return decorated_function
return decorator
端點裝飾器¶
當您想要使用 werkzeug 路由系統以獲得更大的靈活性時,您需要將 Rule
中定義的端點對應到視圖函數。這可以使用此裝飾器來實現。例如
from flask import Flask
from werkzeug.routing import Rule
app = Flask(__name__)
app.url_map.add(Rule('/', endpoint='index'))
@app.endpoint('index')
def my_index():
return "Hello world"