Flask 視圖裝飾器

2021-08-11 10:09 更新

Python 擁有一件非常有趣的特性,那就是函數(shù)裝飾器。這個(gè)特性允許您使用一些 非常簡(jiǎn)介的語法編輯 Web 應(yīng)用。因?yàn)?Flask 中的每個(gè)視圖都是一個(gè)函數(shù)裝飾器, 這些裝飾器被用來將附加的功能注入到一個(gè)或者多個(gè)函數(shù)中。 route() 裝飾器您可能已經(jīng)使用過了。但是在一些情況下您需要實(shí)現(xiàn)自己的裝飾器。例如, 您有一個(gè)僅供登陸后的用戶訪問的視圖,如果未登錄的用戶試圖訪問,則把用戶 轉(zhuǎn)接到登陸界面。這個(gè)例子很好地說明了裝飾器的用武之地。

過濾未登錄用戶的裝飾器

現(xiàn)在讓我們實(shí)現(xiàn)一個(gè)這樣的裝飾器。裝飾器是指返回函數(shù)的函數(shù),它其實(shí)非常簡(jiǎn)單。 您僅需要記住,當(dāng)實(shí)現(xiàn)一個(gè)類似的東西,其實(shí)是更新 __name__ 、 __module__ 以及函數(shù)的其他一些屬性,這件事情經(jīng)常被遺忘。但是您不必親自動(dòng)手,這里 有一個(gè)專門用于處理這些的以裝飾器形式調(diào)用的函數(shù)(functools.wraps() )。

這個(gè)例子家丁登陸頁面的名字是 'login' 并且當(dāng)前用戶被保存在 g.user 當(dāng)中, 如果么有用戶登陸, g.user 會(huì)是 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

所以您怎么使用這些裝飾器呢?將它加為視圖函數(shù)外最里層的裝飾器。當(dāng)添加更多 裝飾器的話,一定要記住 route() 考試最外面的:

@app.route('/secret_page')
@login_required
def secret_page():
    pass

緩存裝飾器

試想你有一個(gè)運(yùn)算量很大的函數(shù),而且您希望能夠?qū)⑸傻慕Y(jié)果在一段時(shí)間內(nèi) 緩存起來,一個(gè)裝飾器將會(huì)非常適合用于干這種事。我們假定您已經(jīng)參考 緩存 中提到的內(nèi)容配置好了緩存功能。

這里有一個(gè)用作例子的緩存函數(shù),它從一個(gè)指定的前綴(通常是一個(gè)格式化字符串) 和當(dāng)前請(qǐng)求的路徑生成一個(gè)緩存鍵。請(qǐng)注意我們創(chuàng)建了一個(gè)這樣的函數(shù): 它先創(chuàng)建 一個(gè)裝飾器,然后用這個(gè)裝飾器包裝目標(biāo)函數(shù)。聽起來很復(fù)雜?不幸的是,這的確 有些難,但是代碼看起來會(huì)非常直接明了。

被裝飾器包裝的函數(shù)將能做到如下幾點(diǎn):

  1. 以當(dāng)前請(qǐng)求和路徑為基礎(chǔ)生成緩存時(shí)使用的鍵。
  2. 從緩存中取出對(duì)應(yīng)鍵的值,如果緩存返回的不是空,我們就將它返回回去。
  3. 如果緩存中沒有這個(gè)鍵,那么最初的函數(shù)將會(huì)被執(zhí)行,并且返回的值在指定時(shí)間 (默認(rèn)5分鐘內(nèi))被緩存起來。

代碼如下:

from functools import wraps
from flask import request

def cached(timeout=5 * 60, key='view/%s'):
    def decorator(f):
        @wraps(f)
        def decorated_function(*args, **kwargs):
            cache_key = key % 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

注意,這段代碼假定一個(gè)示例用的 cache 對(duì)象時(shí)可用的。請(qǐng)參考 緩存 以獲取更多信息。

模板裝飾器

TurboGears 的家伙們前一段時(shí)間發(fā)明了一種新的常用范式,那就是模板裝飾器。 這個(gè)裝飾器的關(guān)鍵在于,您將想要傳遞給模板的值組織成字典的形式,然后從 視圖函數(shù)中返回,這個(gè)模板將會(huì)被自動(dòng)渲染。這樣,下面的三個(gè)例子就是等價(jià)的了:

@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)

正如您所看到的,如果沒有模板名被指定,那么他會(huì)使用 URL 映射的最后一部分, 然后將點(diǎn)轉(zhuǎn)換為反斜杠,最后添加上 '.html' 作為模板的名字。當(dāng)裝飾器 包裝的函數(shù)返回,返回的字典就會(huì)被傳遞給模板渲染函數(shù)。如果 None 被返回 了,那么相當(dāng)于一個(gè)空的字典。如果非字典類型的對(duì)象被返回,函數(shù)將照原樣 將那個(gè)對(duì)象再次返回。這樣您就可以繼續(xù)使用重定向函數(shù)或者返回簡(jiǎn)單的字符串了。

這是那個(gè)裝飾器的源代碼:

from functools import wraps
from flask import request

def templated(template=None):
    def decorator(f):
        @wraps(f)
        def decorated_function(*args, **kwargs):
            template_name = template
            if template_name is None:
                template_name = 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 路由系統(tǒng)來獲得更多的靈活性。您需要將終點(diǎn)(Endpoint) 像 Rule 中定義的那樣映射起來。通過一個(gè)裝飾器 是可以做到的,例如:

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"


以上內(nèi)容是否對(duì)您有幫助:
在線筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號(hào)
微信公眾號(hào)

編程獅公眾號(hào)