在线观看不卡亚洲电影_亚洲妓女99综合网_91青青青亚洲娱乐在线观看_日韩无码高清综合久久

鍍金池/ 教程/ Python/ 快速上手
應(yīng)用環(huán)境
配置管理
大型應(yīng)用
可插撥視圖
Flask 方案
在 Shell 中使用 Flask
針對(duì)高級(jí)程序員的前言
使用藍(lán)圖的模塊化應(yīng)用
部署方式
信號(hào)
排除應(yīng)用錯(cuò)誤
模板
請(qǐng)求環(huán)境
掌握應(yīng)用錯(cuò)誤
測(cè)試 Flask 應(yīng)用
前言
教程
安裝
快速上手
Flask 擴(kuò)展

快速上手

等久了吧?本文會(huì)給你好好介紹如何上手 Flask 。這里假定你已經(jīng)安裝好了 Flask , 否則請(qǐng)先閱讀《 安裝 》。

一個(gè)最小的應(yīng)用

一個(gè)最小的 Flask 應(yīng)用如下:

from flask import Flask
app = Flask(__name__)

@app.route('/')
def hello_world():
    return 'Hello World!'

if __name__ == '__main__':
    app.run()

把它保存為 hello.py 或其他類(lèi)似名稱(chēng)并用你的 Python 解釋器運(yùn)行這個(gè)文件。請(qǐng)不要使用 flask.py 作為應(yīng)用名稱(chēng),這會(huì)與 Flask 本身發(fā)生沖突。

$ python hello.py
 * Running on http://127.0.0.1:5000/

現(xiàn)在,在瀏覽器中打開(kāi) http://127.0.0.1:5000/ ,就可以看到問(wèn)候頁(yè)面了。

那么,這些代碼是什么意思呢?

  1. 首先我們導(dǎo)入了 Flask 類(lèi)。這個(gè)類(lèi)的實(shí)例將會(huì)成為我們的 WSGI 應(yīng)用。

  2. 接著我們創(chuàng)建了這個(gè)類(lèi)的實(shí)例。第一個(gè)參數(shù)是應(yīng)用模塊或者包的名稱(chēng)。如果你使用一個(gè)單一模塊(就像本例),那么應(yīng)當(dāng)使用 __name__ ,因?yàn)槊Q(chēng)會(huì)根據(jù)這個(gè)模塊是按應(yīng)用方式使用還是作為一個(gè)模塊導(dǎo)入而發(fā)生變化(可能是 __main__ ,也可能是 實(shí)際導(dǎo)入的名稱(chēng))。這個(gè)參數(shù)是必需的,這樣 Flask 就可以知道在哪里找到模板和靜態(tài)文件等東西。更多內(nèi)容詳見(jiàn) Flask 文檔。

  3. 然后我們使用 route() 裝飾器來(lái)告訴 Flask 觸發(fā)函數(shù)的 URL 。

  4. 函數(shù)名稱(chēng)可用于生成相關(guān)聯(lián)的 URL ,并返回需要在用戶(hù)瀏覽器中顯示的信息。

  5. 最后,使用 run() 函數(shù)來(lái)運(yùn)行本地服務(wù)器和我們的應(yīng)用。 if __name__ == '__main__’: 確保服務(wù)器只會(huì)在使用 Python 解釋器運(yùn)行代碼的 情況下運(yùn)行,而不會(huì)在作為模塊導(dǎo)入時(shí)運(yùn)行。

按 control-C 可以停止服務(wù)器。

外部可見(jiàn)的服務(wù)器。

運(yùn)行服務(wù)器后,會(huì)發(fā)現(xiàn)只有你自己的電腦可以使用服務(wù),而網(wǎng)絡(luò)中的其他電腦卻不行。 缺省設(shè)置就是這樣的,因?yàn)樵谡{(diào)試模式下該應(yīng)用的用戶(hù)可以執(zhí)行你電腦中的任意 Python 代碼。

如果你關(guān)閉了 調(diào)試 或信任你網(wǎng)絡(luò)中的用戶(hù),那么可以讓服務(wù)器被公開(kāi)訪問(wèn)。只要像這樣改變 run() 方法的調(diào)用:

app.run(host='0.0.0.0')

這行代碼告訴你的操作系統(tǒng)監(jiān)聽(tīng)一個(gè)公開(kāi)的 IP 。

調(diào)試模式

雖然 run() 方法可以方便地啟動(dòng)一個(gè)本地開(kāi)發(fā)服務(wù)器,但是每次修改應(yīng)用之后都需要手動(dòng)重啟服務(wù)器。這樣不是很方便, Flask 可以做得更好。如果你打開(kāi)調(diào)試模式,那么服務(wù)器會(huì)在修改應(yīng)用之后自動(dòng)重啟,并且當(dāng)應(yīng)用出錯(cuò)時(shí)還會(huì)提供一個(gè)有用的調(diào)試器。

打開(kāi)調(diào)試模式有兩種方法,一種是在應(yīng)用對(duì)象上設(shè)置標(biāo)志:

app.debug = True
app.run()

另一種是作為參數(shù)傳遞給 run 方法:

app.run(debug=True)

兩種方法的效果相同。

注意

雖然交互調(diào)試器不能在分布環(huán)境下工作(這使得它基本不可能用于生產(chǎn)環(huán)境),但是它允許執(zhí)行任意代碼,這樣會(huì)成為一個(gè)重大安全隱患。因此, 絕對(duì)不能在生產(chǎn)環(huán)境中使用調(diào)試器

運(yùn)行的調(diào)試器的截圖:

http://wiki.jikexueyuan.com/project/flask-guide/images/4-1.png" alt="" />

想使用其他調(diào)試器?請(qǐng)參閱使用調(diào)試器

路由

現(xiàn)代 web 應(yīng)用都使用漂亮的 URL ,有助于人們記憶,對(duì)于使用網(wǎng)速較慢的移動(dòng)設(shè)備尤其有利。如果用戶(hù)可以不通過(guò)點(diǎn)擊首頁(yè)而直達(dá)所需要的頁(yè)面,那么這個(gè)網(wǎng)頁(yè)會(huì)更得到用戶(hù)的青睞,提高回頭率。

如前文所述, route() 裝飾器用于把一個(gè)函數(shù)綁定到一個(gè) URL 。 下面是一些基本的例子:

@app.route('/')
def index():
    return 'Index Page'

@app.route('/hello')
def hello():
    return 'Hello World'

但是能做的不僅僅是這些!你可以動(dòng)態(tài)變化 URL 的某些部分,還可以為一個(gè)函數(shù)指定多個(gè)規(guī)則。

變量規(guī)則

通過(guò)把 URL 的一部分標(biāo)記為 就可以在 URL 中添加變量。標(biāo)記的部分會(huì)作為關(guān)鍵字參數(shù)傳遞給函數(shù)。通過(guò)使用 ,可以 選擇性的加上一個(gè)轉(zhuǎn)換器,為變量指定規(guī)則。請(qǐng)看下面的例子:

@app.route('/user/<username>')
def show_user_profile(username):
    # show the user profile for that user
    return 'User %s' % username

@app.route('/post/<int:post_id>')
def show_post(post_id):
    # show the post with the given id, the id is an integer
    return 'Post %d' % post_id

現(xiàn)有的轉(zhuǎn)換器有:

類(lèi)型 說(shuō)明
int 接受整數(shù)
float 接受浮點(diǎn)數(shù)
path 和缺省情況相同,但也接受斜杠

唯一的 URL / 重定向行為

Flask 的 URL 規(guī)則都是基于 Werkzeug 的路由模塊的。其背后的理念是保證漂亮的外觀和唯一的 URL 。這個(gè)理念來(lái)自于 Apache 和更早期的服務(wù)器。

假設(shè)有如下兩條規(guī)則:

@app.route('/projects/')
def projects():
    return 'The project page'

@app.route('/about')
def about():
    return 'The about page'

它們看上去很相近,不同之處在于 URL 定義 中尾部的斜杠。第一個(gè)例子中 prjects 的 URL 是中規(guī)中舉的,尾部有一個(gè)斜杠,看起來(lái)就如同一個(gè)文件夾。訪問(wèn)一個(gè)沒(méi)有斜杠結(jié)尾的 URL 時(shí) Flask 會(huì)自動(dòng)進(jìn)行重定向,幫你在尾部加上一個(gè)斜杠。

但是在第二個(gè)例子中, URL 沒(méi)有尾部斜杠,因此其行為表現(xiàn)與一個(gè)文件類(lèi)似。如果訪問(wèn)這個(gè) URL 時(shí)添加了尾部斜杠就會(huì)得到一個(gè) 404 錯(cuò)誤。

為什么這樣做?因?yàn)檫@樣可以在省略末尾斜杠時(shí)仍能繼續(xù)相關(guān)的 URL 。這種重定向行為與 Apache 和其他服務(wù)器一致。同時(shí), URL 仍保持唯一,幫助搜索引擎不重復(fù)索引同一頁(yè)面。

URL 構(gòu)建

如果可以匹配 URL ,那么 Flask 也可以生成 URL 嗎?當(dāng)然可以。 url_for() 函數(shù)就是用于構(gòu)建指定函數(shù)的 URL 的。它把函數(shù)名稱(chēng)作為 第一個(gè)參數(shù),其余參數(shù)對(duì)應(yīng) URL 中的變量。未知變量將添加到 URL 中作為查詢(xún)參數(shù)。 例如:

>>> from flask import Flask, url_for
>>> app = Flask(__name__)
>>> @app.route('/')
... def index(): pass
...
>>> @app.route('/login')
... def login(): pass
...
>>> @app.route('/user/<username>')
... def profile(username): pass
...
>>> with app.test_request_context():
...  print url_for('index')
...  print url_for('login')
...  print url_for('login', next='/')
...  print url_for('profile', username='John Doe')
...
/
/login
/login?next=/
/user/John%20Doe

(例子中還使用下文要講到的 test_request_context() 方法。這個(gè)方法的作用是告訴 Flask 我們正在處理一個(gè)請(qǐng)求,而實(shí)際上也許我們正處在交互 Python shell 之中,并沒(méi)有真正的請(qǐng)求。詳見(jiàn)下面的本地環(huán)境 )。

為什么不在把 URL 寫(xiě)死在模板中,反而要?jiǎng)討B(tài)構(gòu)建?有三個(gè)很好的理由:

  1. 反向解析通常比硬編碼 URL 更直觀。同時(shí),更重要的是你可以只在一個(gè)地方改變 URL ,而不用到處亂找。

  2. URL 創(chuàng)建會(huì)為你處理特殊字符的轉(zhuǎn)義和 Unicode 數(shù)據(jù),不用你操心。

  3. 如果你的應(yīng)用是放在 URL 根路徑之外的地方(如在 /myapplication 中,不在 / 中), url_for() 會(huì)為你妥善處理。

HTTP 方法

HTTP ( Web 應(yīng)用使用的協(xié)議) 協(xié)議中有訪問(wèn) URL 的不同方法。缺省情況下,一個(gè)路由 只回應(yīng) GET 請(qǐng)求,但是可以通過(guò) methods 參數(shù)使用不同方法。例如:

@app.route('/login', methods=['GET', 'POST'])
def login():
    if request.method == 'POST':
        do_the_login()
    else:
        show_the_login_form()

如果當(dāng)前使用的是 GET 方法,會(huì)自動(dòng)添加 HEAD ,你不必親自操刀。同時(shí)還會(huì)確保 HEAD 請(qǐng)求按照 HTTP RFC (說(shuō)明 HTTP 協(xié)議的文檔)的要求來(lái)處理,因此你可以 完全忽略這部分 HTTP 規(guī)范。與 Flask 0.6 一樣, OPTIONS 自動(dòng)為你處理好。

完全不懂 HTTP 方法?沒(méi)關(guān)系,這里給你速成培訓(xùn)一下:

HTTP 方法(通常也被稱(chēng)為“動(dòng)作”)告訴服務(wù)器一個(gè)頁(yè)面請(qǐng)求要做 什么。以下是常見(jiàn)的方法:

GET

瀏覽器告訴服務(wù)器只要得到頁(yè)面上的信息并發(fā)送這些信息。這可能是最常見(jiàn)的方法。

HEAD

瀏覽器告訴服務(wù)器想要得到信息,但是只要得到信息頭 就行了,頁(yè)面內(nèi)容不要。 一個(gè)應(yīng)用應(yīng)該像接受到一個(gè) GET 請(qǐng)求一樣運(yùn)行,但是不傳遞實(shí)際的內(nèi)容。在 Flask 中,你根本不必理會(huì)這個(gè),下層的 Werkzeug 庫(kù)會(huì)為你處理好。

POST

瀏覽器告訴服務(wù)器想要向 URL 發(fā)表一些新的信息,服務(wù)器必須確保數(shù)據(jù)被保存好且只保存了一次。 HTML 表單實(shí)際上就是使用這個(gè)訪求向服務(wù)器傳送數(shù)據(jù)的。

PUT

與 POST 方法類(lèi)似,不同的是服務(wù)器可能觸發(fā)多次儲(chǔ)存過(guò)程而把舊的值覆蓋掉。你 可能會(huì)問(wèn)這樣做有什么用?這樣做是有原因的。假設(shè)在傳輸過(guò)程中連接丟失的情況 下,一個(gè)處于瀏覽器和服務(wù)器之間的系統(tǒng)可以在不中斷的情況下安全地接收第二次請(qǐng)求。在這種情況下,使用 POST 方法就無(wú)法做到了,因?yàn)樗槐挥|發(fā)一次。

DELETE

刪除給定位置的信息。

OPTIONS

為客戶(hù)端提供一個(gè)查詢(xún) URL 支持哪些方法的捷徑。從 Flask 0.6 開(kāi)始,自動(dòng)為你實(shí)現(xiàn)了這個(gè)方法。 有趣的是在 HTML4 和 XHTML1 中,表單只能使用 GET 和 POST 方法。但是 JavaScript 和未來(lái)的 HTML 標(biāo)準(zhǔn)中可以使用其他的方法。此外, HTTP 近來(lái)已經(jīng)變得相當(dāng) 流行,瀏覽器不再只是唯一使用 HTTP 的客戶(hù)端。比如許多版本控制系統(tǒng)也使用 HTTP 。

靜態(tài)文件

動(dòng)態(tài)的 Web 應(yīng)用也需要靜態(tài)文件,一般是 CSS 和 JavaScript 文件。理想情況下你的 服務(wù)器已經(jīng)配置好了為你的提供靜態(tài)文件的服務(wù)。在開(kāi)發(fā)過(guò)程中, Flask 也能做好這個(gè)工作。只要在你的包或模塊旁邊創(chuàng)建一個(gè)名為 static 的文件夾就行了。靜態(tài)文件位于應(yīng)用的 /static 中。

使用選定的 'static' 端點(diǎn)就可以生成相應(yīng)的 URL 。:

url_for('static', filename='style.css')

這個(gè)靜態(tài)文件在文件系統(tǒng)中的位置應(yīng)該是 static/style.css

渲染模板

在 Python 內(nèi)部生成 HTML 不好玩,且相當(dāng)笨拙。因?yàn)槟惚仨氉约贺?fù)責(zé) HTML 轉(zhuǎn)義,以確保應(yīng)用的安全。因此, Flask 自動(dòng)為你配置的 Jinja2 模板引擎。

使用 render_template() 方法可以渲染模板,你只要提供模板名稱(chēng)和需要 作為參數(shù)傳遞給模板的變量就行了。下面是一個(gè)簡(jiǎn)單的模板渲染例子:

from flask import render_template

@app.route('/hello/')
@app.route('/hello/<name>')
def hello(name=None):
    return render_template('hello.html', name=name)

Flask 會(huì)在 templates 文件夾內(nèi)尋找模板。因此,如果你的應(yīng)用是一個(gè)模塊,那么模板 文件夾應(yīng)該在模塊旁邊;如果是一個(gè)包,那么就應(yīng)該在包里面:

情形 1: 一個(gè)模塊:

/application.py
/templates
    /hello.html

情形 2: 一個(gè)包:

/application
    /__init__.py
    /templates
        /hello.html

你可以充分使用 Jinja2 模板引擎的威力。更多內(nèi)容,詳見(jiàn)官方 Jinja2 模板文檔

模板舉例:

<!doctype html>
<title>Hello from Flask</title>
{% if name %}
  <h1>Hello {{ name }}!</h1>
{% else %}
  <h1>Hello World!</h1>
{% endif %}

在模板內(nèi)部你也可以訪問(wèn) request 、sessiong [1] 對(duì)象,以及 get_flashed_messages() 函數(shù)。

模板在繼承使用的情況下尤其有用,其工作原理 模板繼承 方案 文檔。簡(jiǎn)單的說(shuō),模板繼承可以使每個(gè)頁(yè)面的特定元素(如頁(yè)頭,導(dǎo)航,頁(yè)尾)保持一致。

自動(dòng)轉(zhuǎn)義默認(rèn)開(kāi)啟。因此,如果 name 包含 HTML ,那么會(huì)被自動(dòng)轉(zhuǎn)義。如果你可以信任某個(gè)變量,且知道它是安全的 HTML (例如變量來(lái)自一個(gè)把 wiki 標(biāo)記轉(zhuǎn)換為 HTML 的模塊),那么可以使用 Markup 類(lèi)把它標(biāo)記為安全的。否則請(qǐng)?jiān)谀0逯惺褂?|safe 過(guò)濾器。更多例子參見(jiàn) Jinja 2 文檔。

下面簡(jiǎn)單介紹一下 Markup 類(lèi)的工作方式:

>>> from flask import Markup
>>> Markup('<strong>Hello %s!</strong>') % '<blink>hacker</blink>'
Markup(u'<strong>Hello &lt;blink&gt;hacker&lt;/blink&gt;!</strong>')
>>> Markup.escape('<blink>hacker</blink>')
Markup(u'&lt;blink&gt;hacker&lt;/blink&gt;')
>>> Markup('<em>Marked up</em> &raquo; HTML').striptags()
u'Marked up \xbb HTML'

Changed in version 0.5: 自動(dòng)轉(zhuǎn)義不再為所有模板開(kāi)啟,只為擴(kuò)展名為 .html 、 .htm 、 .xml 和 .xhtml 開(kāi)啟。從字符串載入的模板將關(guān)閉自動(dòng)轉(zhuǎn)義。

[1] 不理解什么是 g 對(duì)象?它是某個(gè)可以根據(jù)需要儲(chǔ)存信息的東西。更多信息參見(jiàn) g 對(duì)象的文檔和在 Flask 中使用 SQLite 3 文檔。

操作請(qǐng)求數(shù)據(jù)

對(duì)于 Web 應(yīng)用來(lái)說(shuō)對(duì)客戶(hù)端向服務(wù)器發(fā)送的數(shù)據(jù)作出響應(yīng)很重要。在 Flask 中由全局對(duì)象 request 來(lái)提供請(qǐng)求信息。如果你有一些 Python 基礎(chǔ),那么可能 會(huì)奇怪:既然這個(gè)對(duì)象是全局的,怎么還能保持線程安全?答案是本地環(huán)境:

本地環(huán)境

內(nèi)部信息

如果你想了解其工作原理和如何測(cè)試,請(qǐng)閱讀本節(jié),否則可以跳過(guò)本節(jié)。 某些對(duì)象在 Flask 中是全局對(duì)象,但是不是通常意義下的全局對(duì)象。這些對(duì)象實(shí)際上是 特定環(huán)境下本地對(duì)象的代理。真拗口!但還是很容易理解的。

設(shè)想現(xiàn)在處于處理線程的環(huán)境中。一個(gè)請(qǐng)求進(jìn)來(lái)了,服務(wù)器決定生成一個(gè)新線程(或者 叫其他什么名稱(chēng)的東西,這個(gè)下層的東西能夠處理包括線程在內(nèi)的并發(fā)系統(tǒng))。當(dāng) Flask 開(kāi)始其內(nèi)部請(qǐng)求處理時(shí)會(huì)把當(dāng)前線程作為活動(dòng)環(huán)境,并把當(dāng)前應(yīng)用和 WSGI 環(huán)境 綁定到這個(gè)環(huán)境(線程)。它以一種聰明的方式使得一個(gè)應(yīng)用可以在不中斷的情況下調(diào)用另一個(gè)應(yīng)用。

這對(duì)你有什么用?基本上你可以完全不必理會(huì)。這個(gè)只有在做單元測(cè)試時(shí)才有用。在測(cè)試 時(shí)會(huì)遇到由于沒(méi)有請(qǐng)求對(duì)象而導(dǎo)致依賴(lài)于請(qǐng)求的代碼會(huì)突然崩潰的情況。對(duì)策是自己創(chuàng)建 一個(gè)請(qǐng)求對(duì)象并綁定到環(huán)境。最簡(jiǎn)單的單元測(cè)試解決方案是使用 test_request_context() 環(huán)境管理器。通過(guò)使用 with 語(yǔ)句可以 綁定一個(gè)測(cè)試請(qǐng)求,以便于交互。例如:

from flask import request

with app.test_request_context('/hello', method='POST'):
    # now you can do something with the request until the
    # end of the with block, such as basic assertions:
    assert request.path == '/hello'
    assert request.method == 'POST'

另一種方式是把整個(gè) WSGI 環(huán)境傳遞給 request_context() 方法:

from flask import request

with app.request_context(environ):
    assert request.method == 'POST'

請(qǐng)求對(duì)象

請(qǐng)求對(duì)象在 API 一節(jié)中有詳細(xì)說(shuō)明這里不細(xì)談(參見(jiàn) request )。 這里簡(jiǎn)略地談一下最常見(jiàn)的操作。首先,你必須從 flask 模塊導(dǎo)入請(qǐng)求對(duì)象:

from flask import request

通過(guò)使用 method 屬性可以操作當(dāng)前請(qǐng)求方法,通過(guò)使用 form 屬性處理表單數(shù)據(jù)。以下是使用兩個(gè)屬性的例子:

@app.route('/login', methods=['POST', 'GET'])
def login():
    error = None
    if request.method == 'POST':
        if valid_login(request.form['username'],
                       request.form['password']):
            return log_the_user_in(request.form['username'])
        else:
            error = 'Invalid username/password'
    # 如果請(qǐng)求訪求是 GET 或驗(yàn)證未通過(guò)就會(huì)執(zhí)行下面的代碼
    return render_template('login.html', error=error)

當(dāng) form 屬性中不存在這個(gè)鍵時(shí)會(huì)發(fā)生什么?會(huì)引發(fā)一個(gè) KeyError 。如果你不像捕捉一個(gè)標(biāo)準(zhǔn)錯(cuò)誤一樣捕捉 KeyError ,那么會(huì)顯示一個(gè) HTTP 400 Bad Request 錯(cuò)誤頁(yè)面。因此,多數(shù)情況下你不必處理這個(gè)問(wèn)題。

要操作 URL (如 ?key=value )中提交的參數(shù)可以使用 args 屬性:

searchword = request.args.get('key', '')

用戶(hù)可能會(huì)改變 URL 導(dǎo)致出現(xiàn)一個(gè) 400 請(qǐng)求出錯(cuò)頁(yè)面,這樣降低了用戶(hù)友好度。因此, 我們推薦使用 get 或通過(guò)捕捉 KeyError 來(lái)訪問(wèn) URL 參數(shù)。

完整的請(qǐng)求對(duì)象方法和屬性參見(jiàn) request 文檔。

文件上傳

用 Flask 處理文件上傳很容易,只要確保不要忘記在你的 HTML 表單中設(shè)置 enctype="multipart/form-data" 屬性就可以了。否則瀏覽器將不會(huì)傳送你的文件。

已上傳的文件被儲(chǔ)存在內(nèi)存或文件系統(tǒng)的臨時(shí)位置。你可以通過(guò)請(qǐng)求對(duì)象 files 屬性來(lái)訪問(wèn)上傳的文件。每個(gè)上傳的文件都儲(chǔ)存在這個(gè) 字典型屬性中。這個(gè)屬性基本和標(biāo)準(zhǔn) Python file 對(duì)象一樣,另外多出一個(gè)用于把上傳文件保存到服務(wù)器的文件系統(tǒng)中的 save() 方法。下例展示其如何運(yùn)作:

from flask import request

@app.route('/upload', methods=['GET', 'POST'])
def upload_file():
    if request.method == 'POST':
        f = request.files['the_file']
        f.save('/var/www/uploads/uploaded_file.txt')
    ...

如果想要知道文件上傳之前其在客戶(hù)端系統(tǒng)中的名稱(chēng),可以使用 filename 屬性。但是請(qǐng)牢記這個(gè)值是 可以偽造的,永遠(yuǎn)不要信任這個(gè)值。如果想要把客戶(hù)端的文件名作為服務(wù)器上的文件名, 可以通過(guò) Werkzeug 提供的 secure_filename() 函數(shù):

from flask import request
from werkzeug import secure_filename

@app.route('/upload', methods=['GET', 'POST'])
def upload_file():
    if request.method == 'POST':
        f = request.files['the_file']
        f.save('/var/www/uploads/' + secure_filename(f.filename))
    ...

更好的例子參見(jiàn) 上傳文件 方案。

Cookies

要訪問(wèn) cookies ,可以使用 cookies 屬性??梢允褂谜?qǐng)求對(duì)象 的 set_cookie 方法來(lái)設(shè)置 cookies 。請(qǐng)求對(duì)象的 cookies 屬性是一個(gè)包含了客戶(hù)端傳輸?shù)乃?cookies 的字典。 在 Flask 中,如果能夠使用 會(huì)話(huà) ,那么就不要直接使用 cookies ,因?yàn)?會(huì)話(huà)比較安全一些。

讀取 cookies:

from flask import request

@app.route('/')
def index():
    username = request.cookies.get('username')
    # 使用 cookies.get(key) 來(lái)代替 cookies[key] ,
    # 以避免當(dāng) cookie 不存在時(shí)引發(fā) KeyError 。

儲(chǔ)存 cookies:

from flask import make_response

@app.route('/')
def index():
    resp = make_response(render_template(...))
    resp.set_cookie('username', 'the username')
    return resp

注意, cookies 設(shè)置在響應(yīng)對(duì)象上。通常只是從視圖函數(shù)返回字符串, Flask 會(huì)把它們 轉(zhuǎn)換為響應(yīng)對(duì)象。如果你想顯式地轉(zhuǎn)換,那么可以使用 make_response() 函數(shù),然后再修改它。

使用延遲的請(qǐng)求回調(diào)方案可以在沒(méi)有響應(yīng)對(duì)象的情況下設(shè)置一個(gè) cookie 。

同時(shí)可以參見(jiàn)關(guān)于響應(yīng) 。

重定向和錯(cuò)誤

使用 redirect() 函數(shù)可以重定向。使用 abort() 可以更早退出請(qǐng)求,并返回錯(cuò)誤代碼:

from flask import abort, redirect, url_for

@app.route('/')
def index():
    return redirect(url_for('login'))

@app.route('/login')
def login():
    abort(401)
    this_is_never_executed()

上例實(shí)際上是沒(méi)有意義的,它讓一個(gè)用戶(hù)從索引頁(yè)重定向到一個(gè)無(wú)法訪問(wèn)的頁(yè)面(401 表示禁止訪問(wèn))。但是上例可以說(shuō)明重定向和出錯(cuò)跳出是如何工作的。

缺省情況下每種出錯(cuò)代碼都會(huì)對(duì)應(yīng)顯示一個(gè)黑白的出錯(cuò)頁(yè)面。使用 errorhandler() 裝飾器可以定制出錯(cuò)頁(yè)面:

from flask import render_template

@app.errorhandler(404)
def page_not_found(error):
    return render_template('page_not_found.html'), 404

注意 render_template() 后面的 404 ,這表示頁(yè)面對(duì)就的出錯(cuò)代碼是 404 ,即頁(yè)面不存在。缺省情況下 200 表示一切正常。

關(guān)于響應(yīng)

視圖函數(shù)的返回值會(huì)自動(dòng)轉(zhuǎn)換為一個(gè)響應(yīng)對(duì)象。如果返回值是一個(gè)字符串,那么會(huì)被轉(zhuǎn)換為一個(gè)包含作為響應(yīng)體的字符串、一個(gè) 200 OK 出錯(cuò)代碼 和一個(gè) text/html MIME 類(lèi)型的響應(yīng)對(duì)象。以下是轉(zhuǎn)換的規(guī)則:

  1. 如果視圖要返回的是一個(gè)響應(yīng)對(duì)象,那么就直接返回它。
  2. 如果要返回的是一個(gè)字符串,那么根據(jù)這個(gè)字符串和缺省參數(shù)生成一個(gè)用于返回的響應(yīng)對(duì)象。
  3. 如果要返回的是一個(gè)元組,那么元組中的項(xiàng)目可以提供額外的信息。元組中必須至少包含一個(gè)項(xiàng)目,且項(xiàng)目應(yīng)當(dāng)由 (response, status, headers) 組成。 status 的值會(huì)重載狀態(tài)代碼, headers 是一個(gè)由額外頭部值組成的列表或字典。
  4. 如果以上都不是,那么 Flask 會(huì)假定返回值是一個(gè)有效的 WSGI 應(yīng)用并把它轉(zhuǎn)換為一個(gè)響應(yīng)對(duì)象。 如果想要在視圖內(nèi)部掌控響應(yīng)對(duì)象的結(jié)果,那么可以使用 make_response() 函數(shù)。

設(shè)想有如下視圖:

@app.errorhandler(404)
def not_found(error):
    return render_template('error.html'), 404

可以使用 make_response() 包裹返回表達(dá)式,獲得響應(yīng)對(duì)象,并對(duì)該對(duì)象進(jìn)行修改,然后再返回:

@app.errorhandler(404)
def not_found(error):
    resp = make_response(render_template('error.html'), 404)
    resp.headers['X-Something'] = 'A value'
    return resp

會(huì)話(huà)

除了請(qǐng)求對(duì)象之外還有一種稱(chēng)為 session 的對(duì)象,允許你在不同請(qǐng)求 之間儲(chǔ)存信息。這個(gè)對(duì)象相當(dāng)于用密鑰簽名加密的 cookie ,即用戶(hù)可以查看你的 cookie ,但是如果沒(méi)有密鑰就無(wú)法修改它。

使用會(huì)話(huà)之前你必須設(shè)置一個(gè)密鑰。舉例說(shuō)明:

from flask import Flask, session, redirect, url_for, escape, request

app = Flask(__name__)

@app.route('/')
def index():
    if 'username' in session:
        return 'Logged in as %s' % escape(session['username'])
    return 'You are not logged in'

@app.route('/login', methods=['GET', 'POST'])
def login():
    if request.method == 'POST':
        session['username'] = request.form['username']
        return redirect(url_for('index'))
    return '''
        <form action="" method="post">
            <p><input type=text name=username>
            <p><input type=submit value=Login>
        </form>
    '''

@app.route('/logout')
def logout():
    # 如果會(huì)話(huà)中有用戶(hù)名就刪除它。
    session.pop('username', None)
    return redirect(url_for('index'))

# 設(shè)置密鑰,復(fù)雜一點(diǎn):
app.secret_key = 'A0Zr98j/3yX R~XHH!jmN]LWX/,?RT'

這里用到的 escape() 是用來(lái)轉(zhuǎn)義的。如果不使用模板引擎就可以像上例 一樣使用這個(gè)函數(shù)來(lái)轉(zhuǎn)義。

如何生成一個(gè)好的密鑰

生成隨機(jī)數(shù)的關(guān)鍵在于一個(gè)好的隨機(jī)種子,困此一個(gè)好的密鑰應(yīng)當(dāng)有足夠的隨機(jī)性。 你的操作系統(tǒng)可以使用一個(gè)隨機(jī)生成器來(lái)生成一個(gè)好的隨機(jī)種子:

>>> import os
>>> os.urandom(24)
'\xfd{H\xe5<\x95\xf9\xe3\x96.5\xd1\x01O<!\xd5\xa2\xa0\x9fR"\xa1\xa8'

只要復(fù)制這個(gè)隨機(jī)種子到你的代碼中就行了。 基于 cookie 的會(huì)話(huà)的說(shuō)明: Flask 會(huì)把會(huì)話(huà)對(duì)象中的值儲(chǔ)存在 cookie 中。在打開(kāi) cookie 的情況下,如果你訪問(wèn)會(huì)話(huà)對(duì)象中沒(méi)有的值,那么會(huì)得到模糊的錯(cuò)誤信息:請(qǐng)檢查 頁(yè)面 cookie 的大小是否與網(wǎng)絡(luò)瀏覽器所支持的大小一致。

消息閃現(xiàn)

一個(gè)好的應(yīng)用和用戶(hù)接口都有良好的反饋,否則到后來(lái)用戶(hù)就會(huì)討厭這個(gè)應(yīng)用。 Flask 通過(guò)閃現(xiàn)系統(tǒng)來(lái)提供了一個(gè)易用的反饋方式。閃現(xiàn)系統(tǒng)的基本工作原理是在請(qǐng)求結(jié)束時(shí) 記錄一個(gè)消息,提供且只提供給下一個(gè)請(qǐng)求使用。通常通過(guò)一個(gè)布局模板來(lái)展現(xiàn)閃現(xiàn)的 消息。

flash() 用于閃現(xiàn)一個(gè)消息。在模板中,使用 get_flashed_messages() 來(lái)操作消息。完整的例子參見(jiàn) 消息閃現(xiàn) 。

日志

New in version 0.3.

有時(shí)候可能會(huì)遇到數(shù)據(jù)出錯(cuò)需要糾正的情況。例如因?yàn)橛脩?hù)篡改了數(shù)據(jù)或客戶(hù)端代碼出錯(cuò) 而導(dǎo)致一個(gè)客戶(hù)端代碼向服務(wù)器發(fā)送了明顯錯(cuò)誤的 HTTP 請(qǐng)求。多數(shù)時(shí)候在類(lèi)似情況下 返回 400 Bad Request 就沒(méi)事了,但也有不會(huì)返回的時(shí)候,而代碼還得繼續(xù)運(yùn)行下去。

這時(shí)候就需要使用日志來(lái)記錄這些不正常的東西了。自從 Flask 0.3 后就已經(jīng)為你配置好了一個(gè)日志工具。

以下是一些日志調(diào)用示例:

app.logger.debug('A value for debugging')
app.logger.warning('A warning occurred (%d apples)', 42)
app.logger.error('An error occurred')

logger 是一個(gè)標(biāo)準(zhǔn)的 Python Logger 類(lèi), 更多信息詳見(jiàn)官方的 logging 文檔

集成 WSGI 中間件

如果想要在應(yīng)用中添加一個(gè) WSGI 中間件,那么可以包裝內(nèi)部的 WSGI 應(yīng)用。假設(shè)為了 解決 lighttpd 的錯(cuò)誤,你要使用一個(gè)來(lái)自 Werkzeug 包的中間件,那么可以這樣做:

from werkzeug.contrib.fixers import LighttpdCGIRootFix
app.wsgi_app = LighttpdCGIRootFix(app.wsgi_app)

部署到一個(gè)網(wǎng)絡(luò)服務(wù)器

準(zhǔn)備好發(fā)布你的新 Flask 應(yīng)用了嗎?作為本文的一個(gè)圓滿(mǎn)結(jié)尾,你可以立即把應(yīng)用部署到 一個(gè)主機(jī)上。下面介紹的是如何把小項(xiàng)目部署到免費(fèi)主機(jī)上。

? Copyright 2013, Armin Ronacher. Created using Sphinx.