Flask 視圖和路由的進(jìn)階技能

視圖裝飾器

Python 裝飾器是用于轉(zhuǎn)換其它函數(shù)的函數(shù)已艰。當(dāng)一個裝飾的函數(shù)被調(diào)用的時候,裝飾器也會被調(diào)用檀蹋。接著裝飾器就會采取行動,修改參數(shù)云芦,停止執(zhí)行或者調(diào)用原始函數(shù)俯逾。我們可以使用裝飾器來包裝視圖,讓它們在執(zhí)行之前運(yùn)行我們希望的代碼焕数。

@decorator_function
def decorated():
    pass

如果你已經(jīng)瀏覽了 Flask 教程,在這個代碼塊的語法看起來很熟悉刨啸。@app.route 是用于為 Flask 應(yīng)用程序的視圖函數(shù)匹配 URLs 的裝飾器堡赔。

讓我們看看其它的裝飾器,你可能會在你的 Flask 應(yīng)用中使用到它們设联。

認(rèn)證

Flask-Login 擴(kuò)展可以很容易地實(shí)現(xiàn)登錄系統(tǒng)善已。除了處理用戶認(rèn)證的細(xì)節(jié),F(xiàn)lask-Login 提供給我們一個裝飾器离例,它用來限制只允許登錄的用戶訪問某些視圖: @login_required换团。

# app.py

from flask import render_template
from flask.ext.login import login_required, current_user


@app.route('/')
def index():
    return render_template("index.html")

@app.route('/dashboard')
@login_required
def account():
    return render_template("account.html")

<strong>
@app.route 應(yīng)該是外層的視圖裝飾器(換句話說, @app.route 應(yīng)該在所有裝飾器的最前面)宫蛆。
</strong>

一個登錄過的用戶才能夠訪問 /dashboard 路由艘包。我們可以配置 Flask-Login的猛,讓未登錄的用戶重定向到一個登錄頁,返回一個 HTTP 401 狀態(tài)碼或者我們想要它做的任何東西想虎。

緩存

想象下在 CNN 以及其它一些新聞網(wǎng)站中提到我們的應(yīng)用程序卦尊,我們可能會在不久之后接收每秒數(shù)千次的請求。我們的主頁針對每一次請求都要多次訪問數(shù)據(jù)庫舌厨,因此所有這些因素都會導(dǎo)致系統(tǒng)越來越慢岂却,用戶訪問等待的時間越來越長。如何才能加快訪問速度裙椭,讓所有的訪客都不會錯過我們的網(wǎng)站躏哩?

有很多好的答案,但是這一章是關(guān)于緩存揉燃,因此我們就來討論它扫尺。確切地來說,我們將要使用 Flask-Cache 擴(kuò)展你雌。這個擴(kuò)展提供我們一個裝飾器器联,我們可以在我們的首頁視圖上使用這個裝飾器用來在一段時間內(nèi)緩存響應(yīng)。

Flask-Cache 可以被配置成與一堆不同的緩存后端一起工作婿崭。一個流行的選擇是 Redis拨拓,Redis 很容易設(shè)置和使用。假設(shè) Flask-Cache 已經(jīng)配置好氓栈,這個代碼塊顯示我們的緩存裝飾器視圖是什么樣子的渣磷。

# app.py

from flask.ext.cache import Cache
from flask import Flask

app = Flask()

# We'd normally include configuration settings in this call
cache = Cache(app)

@app.route('/')
@cache.cached(timeout=60)
def index():
    [...] # Make a few database calls to get the information we need
    return render_template(
        'index.html',
        latest_posts=latest_posts,
        recent_users=recent_users,
        recent_photos=recent_photos
    )

現(xiàn)在函數(shù)將每 60 秒會執(zhí)行一次,因?yàn)?60 秒后緩存就過期授瘦。響應(yīng)將會保存在我們的緩存中醋界,在緩存沒有過期之前,所有針對首頁的請求都會直接從緩存中讀取提完。

Flask-Cache 也為我們提供了 memoize 函數(shù) — 或者緩存一個函數(shù)調(diào)用某些參數(shù)的結(jié)果形纺。你甚至可以緩存計算開銷很高的 Jinja2 模板片段。

自定義裝飾器

對于這篇文章徒欣,先讓我們想象下我們有一個應(yīng)用程序逐样,該應(yīng)用程序每個月都會向用戶收費(fèi)。如果用戶的賬號已經(jīng)過期打肝,我們將會重定向到收費(fèi)頁面并且讓用戶升級脂新。

# myapp/util.py

from functools import wraps
from datetime import datetime

from flask import flash, redirect, url_for

from flask.ext.login import current_user

def check_expired(func):
    @wraps(func)
    def decorated_function(*args, **kwargs):
        if datetime.utcnow() > current_user.account_expires:
            flash("Your account has expired. Update your billing info.")
            return redirect(url_for('account_billing'))
        return func(*args, **kwargs)

    return decorated_function
  • 當(dāng)一個函數(shù)使用 @check_expired 裝飾,check_expired() 被調(diào)用并且被裝飾的 函數(shù)被作為參數(shù)進(jìn)行傳遞粗梭。
  • @wraps 是一個裝飾器争便,它做了一些工作使得 decorated_function() 看起來像 func()。這使得函數(shù)的行為多了幾分自然断医。
  • decorated_function 將會獲取所有我們傳遞給原始視圖函數(shù) func()argskwargs滞乙。我們在這里檢查用戶的賬號是否過期奏纪。如果已經(jīng)過期的話, 我們將會閃現(xiàn)一條消息并且重定向到一個收費(fèi)頁面酷宵。
  • 現(xiàn)在我們已經(jīng)做了我們想要做的事情亥贸,我們使用它原始的參數(shù)運(yùn)行被裝飾的視圖函數(shù) func()

當(dāng)我們疊加裝飾器的時候浇垦,最上層的裝飾器會首先運(yùn)行炕置,接著調(diào)用下一行的下一個函數(shù):要么是視圖函數(shù),要么就是裝飾器男韧。裝飾器的語法只是 Python 提供的一個語法糖朴摊。

# This code:
@foo
@bar
def one():
    pass

r1 = one()

# is the same as this code:
def two():
    pass

two = foo(bar(two))
r2 = two()

r1 == r2 # True

此代碼塊展示了一個使用我們自定義的裝飾器和來自 Flask-Login 擴(kuò)展的 @login_required 裝飾器的示例。我們可以通過疊加使用多個裝飾器此虑。

# myapp/views.py

from flask import render_template

from flask.ext.login import login_required

from . import app
from .util import check_expired

@app.route('/use_app')
@login_required
@check_expired
def use_app():
    """Use our amazing app."""
    # [...]
    return render_template('use_app.html')

@app.route('/account/billing')
@login_required
def account_billing():
    """Update your billing info."""
    # [...]
    return render_template('account/billing.html')

現(xiàn)在當(dāng)一個用戶試圖訪問 /use_app甚纲,check_expired() 將會確保在運(yùn)行視圖函數(shù)之前用戶的賬號沒有過期。

URL 轉(zhuǎn)換器(converters)

內(nèi)置轉(zhuǎn)換器(converters)

當(dāng)你在 Flask 中定義路由的時候朦前,你可以指定路由的一部分介杆,它們將會轉(zhuǎn)換成 Python 變量并且傳遞到視圖函數(shù)。

@app.route('/user/<username>')
def profile(username):
    pass

在 URL 中的 <username> 將會作為 username 參數(shù)傳入到視圖韭寸。你也可以指定一個轉(zhuǎn)換器春哨,用來在變量傳入視圖之前對其進(jìn)行過濾篩選。

@app.route('/user/id/<int:user_id>')
def profile(user_id):
    pass

在這個代碼塊中恩伺,URL:http://myapp.com/user/id/Q29kZUxlc3NvbiEh 將會返回一個 404 狀態(tài)碼 – 未找到赴背。這是因?yàn)?URL 中的 user_id 要求的是一個整數(shù)但實(shí)際上是一個字符串。

我們也可以有第二個視圖用來處理 user_id 為字符串晶渠,/user/id/Q29kZUxlc3NvbiEh/ 可以調(diào)用該視圖而 /user/id/124 可以調(diào)用第一個視圖凰荚。

下面描述了 Flask 內(nèi)置的 URL 轉(zhuǎn)換器。

  • string: 不帶斜杠(默認(rèn)值)的任何文本褒脯。
  • int: 整數(shù)便瑟。
  • float: 像 int,但是只允許浮點(diǎn)值番川。
  • path:像字符串到涂,但是包含斜杠。

自定義轉(zhuǎn)換器(converters)

我們也能準(zhǔn)備自定義轉(zhuǎn)換器來滿足自己的需求爽彤。在 Reddit 上 — 一個受歡迎的鏈接共享網(wǎng)站 — 用戶創(chuàng)建和主持的以主題為基礎(chǔ)的討論和鏈接共享的社區(qū)养盗。例如缚陷,/r/python 和 /r/flask 就是分別用 URL:redit.com/r/python 和 reddit.com/r/flask 來表示适篙。Reddit 一個有意思的功能就是你可以查看多個 subreddits 的文章,通過在 URL 中使用加號(+)來連接每一個 subreddits 的名稱箫爷,例如嚷节,reddit.com/r/python+flask聂儒。

我們可以在我們自己的 Flask 應(yīng)用程序中使用一個自定義的轉(zhuǎn)換器來實(shí)現(xiàn)這個功能。我們將接受通過加號(+)分離的任意數(shù)量的元素硫痰,轉(zhuǎn)換它們成一個列表(這里實(shí)現(xiàn)了一個叫做 ListConverter 的類)并且把列表元素傳給視圖函數(shù)衩婚。

# myapp/util.py

from werkzeug.routing import BaseConverter

class ListConverter(BaseConverter):

    def to_python(self, value):
        return value.split('+')

    def to_url(self, values):
        return '+'.join(BaseConverter.to_url(value)
                        for value in values)

我們需要定義兩個方法:to_python()to_url()。正如名稱暗示的一樣效斑,to_python() 是用于轉(zhuǎn)換 URL 中的路徑成為一個 Python 對象非春,該對象將會傳遞給視圖;to_url() 是被 url_for() 用來把參數(shù)轉(zhuǎn)換為合適的形式的 URL缓屠。

為了使用我們的 ListConverter奇昙,我們首先必須告訴 Flask 它的存在。

# /myapp/__init__.py

from flask import Flask

app = Flask(__name__)

from .util import ListConverter

app.url_map.converters['list'] = ListConverter

這里可能有機(jī)會碰到循環(huán)導(dǎo)入的問題如果你的 util 模塊有 from . import app 這一行敌完。這是我為什么要等到 app 已經(jīng)初始化后才導(dǎo)入 ListConverter储耐。

現(xiàn)在我們就可以像使用內(nèi)置的轉(zhuǎn)換器一樣使用自己的轉(zhuǎn)換器。我們可以在 @app.route() 中使用 “l(fā)ist”滨溉,就像使用內(nèi)置的 int什湘,float,string晦攒,path 一樣闽撤。

# myapp/views.py

from . import app

@app.route('/r/\\<list:subreddits\\>')
def subreddit_home(subreddits):
    """Show all of the posts for the given subreddits."""
    posts = []
    for subreddit in subreddits:
        posts.extend(subreddit.posts)

    return render_template('/r/index.html', posts=posts)

這應(yīng)該會像 Reddit 的多 reddit 系統(tǒng)一樣工作。同樣的方法可以被使用來做我們想要的任何 URL 轉(zhuǎn)換勤家。

摘要

  • Flask-Login 中的 @login_required 裝飾器幫助你限制只允許登錄的用戶訪問視圖腹尖。
  • Flask-Cache 擴(kuò)展為你提供了大量的裝飾器用來實(shí)現(xiàn)各種的緩存方法。
  • 我們能夠開發(fā)自定義視圖裝飾器用來幫助我們組織代碼并且堅持 DRY(不要重復(fù)你自己)的編碼原則伐脖。
  • 自定義的 URL 轉(zhuǎn)換器是實(shí)現(xiàn)涉及到 URL 的創(chuàng)新功能的一個很好的方式热幔。
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市讼庇,隨后出現(xiàn)的幾起案子绎巨,更是在濱河造成了極大的恐慌,老刑警劉巖蠕啄,帶你破解...
    沈念sama閱讀 206,311評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件场勤,死亡現(xiàn)場離奇詭異,居然都是意外死亡歼跟,警方通過查閱死者的電腦和手機(jī)和媳,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,339評論 2 382
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來哈街,“玉大人留瞳,你說我怎么就攤上這事∩兀” “怎么了她倘?”我有些...
    開封第一講書人閱讀 152,671評論 0 342
  • 文/不壞的土叔 我叫張陵璧微,是天一觀的道長。 經(jīng)常有香客問我硬梁,道長前硫,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 55,252評論 1 279
  • 正文 為了忘掉前任荧止,我火速辦了婚禮屹电,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘跃巡。我一直安慰自己嗤详,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,253評論 5 371
  • 文/花漫 我一把揭開白布瓷炮。 她就那樣靜靜地躺著葱色,像睡著了一般。 火紅的嫁衣襯著肌膚如雪娘香。 梳的紋絲不亂的頭發(fā)上苍狰,一...
    開封第一講書人閱讀 49,031評論 1 285
  • 那天,我揣著相機(jī)與錄音烘绽,去河邊找鬼淋昭。 笑死,一個胖子當(dāng)著我的面吹牛安接,可吹牛的內(nèi)容都是我干的翔忽。 我是一名探鬼主播,決...
    沈念sama閱讀 38,340評論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼盏檐,長吁一口氣:“原來是場噩夢啊……” “哼歇式!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起胡野,我...
    開封第一講書人閱讀 36,973評論 0 259
  • 序言:老撾萬榮一對情侶失蹤材失,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后硫豆,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體龙巨,經(jīng)...
    沈念sama閱讀 43,466評論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,937評論 2 323
  • 正文 我和宋清朗相戀三年熊响,在試婚紗的時候發(fā)現(xiàn)自己被綠了旨别。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,039評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡汗茄,死狀恐怖秸弛,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤胆屿,帶...
    沈念sama閱讀 33,701評論 4 323
  • 正文 年R本政府宣布,位于F島的核電站偶宫,受9級特大地震影響非迹,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜纯趋,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,254評論 3 307
  • 文/蒙蒙 一憎兽、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧吵冒,春花似錦纯命、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,259評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至揪阿,卻和暖如春疗我,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背南捂。 一陣腳步聲響...
    開封第一講書人閱讀 31,485評論 1 262
  • 我被黑心中介騙來泰國打工吴裤, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人溺健。 一個月前我還...
    沈念sama閱讀 45,497評論 2 354
  • 正文 我出身青樓麦牺,卻偏偏與公主長得像,于是被迫代替她去往敵國和親鞭缭。 傳聞我的和親對象是個殘疾皇子剖膳,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,786評論 2 345

推薦閱讀更多精彩內(nèi)容

  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理,服務(wù)發(fā)現(xiàn)岭辣,斷路器潮秘,智...
    卡卡羅2017閱讀 134,599評論 18 139
  • 22年12月更新:個人網(wǎng)站關(guān)停,如果仍舊對舊教程有興趣參考 Github 的markdown內(nèi)容[https://...
    tangyefei閱讀 35,160評論 22 257
  • 第二部分 Blog例子 第八章 用戶驗(yàn)證 大部分程序需要追蹤用戶身份易结。當(dāng)用戶連接到程序枕荞,通過一系列步驟使自己的身份...
    易木成華閱讀 1,281評論 0 4
  • 最近在學(xué)習(xí)flask,用到flask-login搞动,發(fā)現(xiàn)網(wǎng)上只有0.1版本的中文文檔躏精,看了官方已經(jīng)0.4了,并且添加...
    ZZES_ZCDC閱讀 5,934評論 3 24
  • 一條腿已殘 是愛情 一條腿茍存 是生活 命運(yùn)之刀 截斷雙腿 他決心為單愛 一刀兩斷 她卻以愛的假肢 苦苦支著 苦苦...
    ecf33a82606f閱讀 317評論 0 0