歡迎來到 Flask 的世界

歡迎來到 Flask 的世界

Flask: web development, one drop at a time

歡迎閱讀 Flask 的文檔。本文檔分紅幾個部分,我推薦您先讀 《 安裝 》,而後讀《 快速上手 》。《 教程 》 比快速上手文檔更詳細一點,該文檔介紹瞭如何建立一個完整(儘管很小)的 Flask 應用。若是你想深刻研究 Flask ,那麼須要閱讀《 API 》。 《 Flask 方案 》中介紹了一些經常使用的解決方案。javascript

Flask 依賴兩個外部庫: Jinja2 模板引擎和 Werkzeug WSGI 套件。這兩個庫的 使用不在本文檔的範圍內,欲知詳情請移步:php

用戶指南

這部分文檔是比較鬆散的,首先介紹了 Flask 的一些背景材料,接着專一於一步一步地 說明如何使用 Flask 進行 Web 開發。css

前言

在使用 Flask 前請閱讀本文。但願本文能夠回答您有關 Flask 的用途和目的,以及是 否應當使用 Flask 等問題。html

「微」是什麼意思?

「微」並不表明整個應用只能塞在一個 Python 文件內,固然塞在單一文件內也是能夠的。 「微」也不表明 Flask 功能不強。微框架中的「微」字表示 Flask 的目標是保持核心既簡 單而又可擴展。 Flask 不會替你作出許多決定,好比選用何種數據庫。相似的決定,如 使用何種模板引擎,是很是容易改變的。 Flask 能夠變成你任何想要的東西,一切恰到 好處,由你作主。html5

缺省狀況下, Flask 不包含數據庫抽象層、表單驗證或者其餘已有的庫能夠處理的東西。 然而, Flask 經過擴展爲你的應用添加這些功能,就如同這些功能是 Flask 原生的同樣。 大量的擴展用以支持數據庫整合、表單驗證、上傳處理和各類開放驗證等等。Flask 多是 「微小」的,但它已經爲知足您的各類生產須要作出了充足的準備。java

配置和慣例

剛起步的時候 Flask 有許多帶有合理缺省值的配置值和慣例。按照慣例,模板和靜態文件 存放在應用的 Python 源代碼樹的子目錄中,名稱分別爲 templates 和 static 。慣 例是能夠改變的,可是你大可沒必要改變,尤爲是剛起步的時候。python

Flask 可持續發展

一旦你開始使用 Flask ,你會發現有各類各樣的擴展可供使用。 Flask 核心開發組會 審查擴展,並保證經過檢驗的擴展能夠在最新版本的 Flask 中可用。mysql

隨着你的代碼庫日益壯大,你能夠自由地決定設計目標。 Flask 會一直提供一個很是 簡約而優秀的膠合層,就像 Python 語言同樣。你能夠自由地使用 SQLAlchemy 執行高級 模式,或者使用其餘數據庫工具,亦可引入非關係數據模型,甚至還能夠利用用於 Python 網絡接口 WSGI 的非框架工具。jquery

Flask 包含許多能夠自定義其行爲的鉤子。考慮到你的定製需求, Flask 的類專爲繼承 而打造。 若是對這一點感興趣,請閱讀 大型應用 一節。若是對 Flask 的設計原則感 興趣,請移步 Flask 的設計思路 。nginx

接下來請閱讀 安裝 、 快速上手 或者 針對高級程序員的前言 。

針對高級程序員的前言

Flask 中的本地線程對象

Flask 的設計原則之一是簡單的任務不該當使用不少代碼,應當能夠簡單地完成,但同時 又不該當把程序員限制得太死。所以,一些 Flask 的設計思路可能會讓某些人以爲吃驚, 或者難以想象。例如, Flask 內部使用本地線程對象,這樣就能夠不用爲了線程安全的 緣故在同一個請求中在函數之間傳遞對象。這種實現方法是很是便利的,可是當用於依賴 注入或者當嘗試重用使用了與請求掛鉤的值的代碼時,須要一個合法的環境。 Flask 項目 對於本地線程是直言不諱的,沒有一點隱藏的意思,而且在使用本地線程時在代碼中進行 了標註和說明。

作網絡開發時要謹慎

作網絡應用開發時,安全要永記在心。

若是你開發了一個網絡應用,那麼可能會讓用戶註冊並把他們的數據保存在服務器上。 用戶把數據託付給了你。哪怕你的應用只是給本身用的,你也會但願數據無缺無損。

不幸的是,網絡應用的安全性是千瘡百孔的,能夠攻擊的方法太多了。 Flask 能夠防護 現代 Web 應用最多見的安全攻擊:跨站代碼攻擊( XSS )。 Flask 和 下層的 Jinja2 模板引擎會保護你免受這種攻擊,除非故意把不安全的 HTML 代碼放進來。可是安全攻擊 的方法依然還有不少。

這裏警示你:在 Web 開發過程當中要時刻注意安全問題。一些安全問題遠比想象的要複雜 得多。咱們有時會低估程序的弱點,直到被一個聰明人利用這個弱點來攻擊咱們的程序。 不要覺得你的應用不重要,還不足以別人來攻擊。沒準是自動化機器人用垃圾郵件或惡意 軟件連接等東西來填滿你寶貴的數據庫。

Flask 與其餘框架相同,你在開發時必須當心謹慎。

Python 3 的狀況

目前, Python 社區正處在改進庫的過程當中,以便於增強對 Python 語言的新迭代的 支持。雖然如今狀況已經有很大改善,可是仍是存在一些問題使用戶難如下決心如今就 轉向 Python 3 。部分緣由是 Python 語言中的變更長時間未經審覈,還有部分緣由是 咱們尚未想好底層 API 針對 Python 3 中 unicode 處理方式的變化應該如何改動。

咱們強烈建議你在開發過程當中使用 Python 2.7 ,同時打開 Python 3 警告。若是你計劃 在近期升級到 Python 3 ,那麼強烈推薦閱讀 如何編寫向前兼容的 Python 代碼 。

若是你必定要使用 Python 3 ,那麼請先閱讀 python3-support 。

接下來,請閱讀 安裝 或 快速上手 。

安裝

Flask 依賴兩個外部庫: Werkzeug 和 Jinja2 。Werkzeug 是一個 WSGI 套件。 WSGI 是 Web 應用與 多種服務器之間的標準 Python 接口,即用於開發,也用於部署。 Jinja2 是用於渲染 模板的。

那麼如何快速在你的計算機上裝好全部東西?本節會介紹多種方法,可是最強力的方法是 使用 virtualenv 。所以,咱們先說 virtualenv 。

不管使用哪一種方法安裝,你都會須要 Python 2.6 或更高版本。所以請確保安裝了最新的 Python 2.x 版本。在 Python 3 下使用 Flask 請參閱 Python 3 支持 。

virtualenv

若是能夠使用 shell ,那麼可能 Virtualenv 是你在開發環境和生產環境都想使用的 東西。

virtualenv 有什麼用?若是你象我同樣熱愛 Python ,那麼除了基於 Flask 的項目外 還會有其餘項目用到 Python 。當項目愈來愈多時就會面對使用不一樣版本的 Python 的 問題,或者至少會遇到使用不一樣版本的 Python 庫的問題。擺在你面前的是:庫經常不能 向後兼容,更不幸的是任何成熟的應用都不是零依賴。若是兩個項目依賴出現衝突, 怎麼辦?

Virtualenv 就是救星!它的基本原理是爲每一個項目安裝一套 Python ,多套 Python 並存。但它不是真正地安裝多套獨立的 Python 拷貝,而是使用了一種巧妙的方法讓不一樣 的項目處於各自獨立的環境中。讓咱們來看看 virtualenv 是如何運行的!

若是你使用 Mac OS X 或 Linux ,那麼能夠使用下面兩條命令中任意一條:

$ sudo easy_install virtualenv

或更高級的:

$ sudo pip install virtualenv

上述命令中的任意一條就能夠安裝好 virtualenv 。也能夠使用軟件包管理器,在 Ubuntu 系統中能夠試試:

$ sudo apt-get install python-virtualenv

若是你使用 Windows 且沒法使用 easy_install ,那麼你必須先安裝它,安裝方法詳見 《 在 Windows 系統中使用 pip 和 distribute 》。安裝好之後運行上述命令,可是要去掉sudo 前綴。

安裝完 virtualenv ,打開一個 shell ,建立本身的環境。我一般建立一個包含 venv 文件夾的項目文件夾:

$ mkdir myproject
$ cd myproject
$ virtualenv venv
New python executable in env/bin/python
Installing setuptools............done.

如今,每次須要使用項目時,必須先激活相應的環境。在 OS X 和 Linux 系統中運行:

$ . venv/bin/activate

Windows 用戶請運行下面的命令:

$ venv\scripts\activate

異曲同工,你如今進入你的 virtualenv (注意查看你的 shell 提示符已經改變了)。

如今能夠開始在你的 virtualenv 中安裝 Flask 了:

$ pip install Flask

幾秒鐘後就安裝好了。

系統全局安裝

雖然這樣作是可行的,雖然我不推薦。只須要以 root 權限運行 pip 就能夠了:

$ sudo pip install Flask

( Windows 系統中請在管理員 shell 中運行,去掉 sudo )。

生活在邊緣

若是你想要使用最新版的 Flask ,那麼有兩種方法:要麼使用 pip 安裝開發版本, 要麼使用 git 檢索。不管哪一種方法,都推薦使用 virtualenv 。

在一個新的 virtualenv 中得到 git 檢索,並在開發模式下運行:

$ git clone http://github.com/mitsuhiko/flask.git
Initialized empty Git repository in ~/dev/flask/.git/
$ cd flask
$ virtualenv venv --distribute
New python executable in venv/bin/python
Installing distribute............done.
$ . venv/bin/activate
$ python setup.py develop
...
Finished processing dependencies for Flask

上述操做會安裝相關依賴庫並在 virtualenv 中激活 git 頭做爲當前版本。而後只要 使用git pull origin 命令就能夠安裝最新版本的 Flask 了。

若是不使用 git ,那麼能夠這樣得到開發版本:

$ mkdir flask
$ cd flask
$ virtualenv venv --distribute
$ . venv/bin/activate
New python executable in venv/bin/python
Installing distribute............done.
$ pip install Flask==dev
...
Finished processing dependencies for Flask==dev

在 Windows 系統中使用 pip 和 distribute

在 Windows 系統中,安裝 easy_install 稍微有點麻煩,但仍是比較簡單的。最簡單的 方法是下載並運行 ez_setup.py 文件。最簡單的運行文件的方法是打開下載文件所在 文件夾,雙擊這個文件。

接下來,經過把 Python 代碼所在文件夾添加到 PATH 環境變量的方法把 easy_install 命令和其餘 Python 代碼添加到命令搜索目錄。操做方法:用鼠標右鍵 點擊桌面上或者開始菜單中的「個人電腦」圖標,在彈出的菜單中點擊「屬性」。而後 點擊「高級系統設置」(若是是 Windows XP ,則點擊「高級」分頁)。接着點擊「環境 變量」按鈕,雙擊「系統變量」一節中的「 Path 」變量。這樣就能夠添加 Python 代碼 所在的文件夾了。 注意,與已經存在的值之間要用分號分隔。假設你在缺省路徑安裝了 Python 2.7 ,那麼就應該添加以下內容:

;C:\Python27\Scripts

至此安裝完成。要檢驗安裝是否正確能夠打開命令提示符,並運行 easy_install 命令。若是你使用 Windows Vista 或 Windows 7 ,並打開了權限控制,會提示你須要 管理員權限。

至此,你安裝好了 easy_install ,接下來就能夠用它來安裝 pip 了:

> easy_install pip

快速上手

等久了吧?本文會給你好好介紹如何上手 Flask 。這裏假定你已經安裝好了 Flask , 不然請先閱讀《 安裝 》。

一個最小的應用

一個最小的 Flask 應用以下:

from flask import Flask app = Flask(__name__) @app.route('/') def hello_world(): return 'Hello World!' if __name__ == '__main__': app.run() 

把它保存爲 hello.py 或其餘相似名稱並用你的 Python 解釋器運行這個文件。請不要 使用 flask.py 做爲應用名稱,這會與 Flask 自己發生衝突。

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

如今,在瀏覽器中打開 http://127.0.0.1:5000/ ,就 能夠看到問候頁面了。

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

  1. 首先咱們導入了 Flask 類。這個類的實例將會成爲咱們的 WSGI 應用。
  2. 接着咱們建立了這個類的實例。第一個參數是應用模塊或者包的名稱。若是你使用一個 單一模塊(就像本例),那麼應當使用 __name__ ,由於名稱會根據這個模塊是按 應用方式使用仍是做爲一個模塊導入而發生變化(多是 '__main__' ,也多是 實際導入的名稱)。這個參數是必需的,這樣 Flask 就能夠知道在哪裏找到模板和 靜態文件等東西。更多內容詳見 Flask 文檔。
  3. 而後咱們使用 route() 裝飾器來告訴 Flask 觸發函數的 URL 。
  4. 函數名稱可用於生成相關聯的 URL ,並返回須要在用戶瀏覽器中顯示的信息。
  5. 最後,使用 run() 函數來運行本地服務器和咱們的應用。 if __name__ =='__main__': 確保服務器只會在使用 Python 解釋器運行代碼的 狀況下運行,而不會在做爲模塊導入時運行。

按 control-C 能夠中止服務器。

外部可見的服務器。

運行服務器後,會發現只有你本身的電腦能夠使用服務,而網絡中的其餘電腦卻不行。 缺省設置就是這樣的,由於在調試模式下該應用的用戶能夠執行你電腦中的任意 Python 代碼。

若是你關閉了 調試 或信任你網絡中的用戶,那麼可讓服務器被公開訪問。只要像 這樣改變 run() 方法的調用:

app.run(host='0.0.0.0') 

這行代碼告訴你的操做系統監聽一個公開的 IP 。

調試模式

雖然 run() 方法能夠方便地啓動一個本地開發服務器,可是每次 修改應用以後都須要手動重啓服務器。這樣不是很方便, Flask 能夠作得更好。若是你 打開調試模式,那麼服務器會在修改應用以後自動重啓,而且當應用出錯時還會提供一個 有用的調試器。

打開調試模式有兩種方法,一種是在應用對象上設置標誌:

app.debug = True app.run() 

另外一種是做爲參數傳遞給 run 方法:

app.run(debug=True) 

兩種方法的效果相同。

注意

雖然交互調試器不能在分佈環境下工做(這使得它基本不可能用於生產環境),可是 它容許執行任意代碼,這樣會成爲一個重大安全隱患。所以, 絕對不能在生產環境 中使用調試器 。

運行的調試器的截圖:

screenshot of debugger in action

想使用其餘調試器?請參閱 使用調試器 。

路由

現代 web 應用都使用漂亮的 URL ,有助於人們記憶,對於使用網速較慢的移動設備尤爲 有利。若是用戶能夠不經過點擊首頁而直達所須要的頁面,那麼這個網頁會更獲得用戶的 青睞,提升回頭率。

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

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

可是能作的不只僅是這些!你能夠動態變化 URL 的某些部分,還能夠爲一個函數指定多個 規則。

變量規則

經過把 URL 的一部分標記爲 <variable_name> 就能夠在 URL 中添加變量。標記的 部分會做爲關鍵字參數傳遞給函數。經過使用 <converter:variable_name> ,能夠 選擇性的加上一個轉換器,爲變量指定規則。請看下面的例子:

@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 

現有的轉換器有:

int 接受整數
float 接受浮點數
path 和缺省狀況相同,但也接受斜槓

惟一的 URL / 重定向行爲

Flask 的 URL 規則都是基於 Werkzeug 的路由模塊的。其背後的理念是保證漂亮的 外觀和惟一的 URL 。這個理念來自於 Apache 和更早期的服務器。

假設有以下兩條規則:

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

它們看上去很相近,不一樣之處在於 URL 定義 中尾部的斜槓。第一個例子中 projects 的 URL 是中規中舉的,尾部有一個斜槓,看起來就如同一個文件夾。訪問 一個沒有斜槓結尾的 URL 時 Flask 會自動進行重定向,幫你在尾部加上一個斜槓。

可是在第二個例子中, URL 沒有尾部斜槓,所以其行爲表現與一個文件相似。若是 訪問這個 URL 時添加了尾部斜槓就會獲得一個 404 錯誤。

爲何這樣作?由於這樣能夠在省略末尾斜槓時仍能繼續相關的 URL 。這種重定向 行爲與 Apache 和其餘服務器一致。同時, URL 仍保持惟一,幫助搜索引擎不重複 索引同一頁面。

URL 構建

若是能夠匹配 URL ,那麼 Flask 也能夠生成 URL 嗎?固然能夠。 url_for() 函數就是用於構建指定函數的 URL 的。它把函數名稱做爲 第一個參數,其他參數對應 URL 中的變量。未知變量將添加到 URL 中做爲查詢參數。 例如:

>>> 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() 方法。這個 方法的做用是告訴 Flask 咱們正在處理一個請求,而實際上也許咱們正處在交互 Python shell 之中,並無真正的請求。詳見下面的 本地環境 )。

爲何不在把 URL 寫死在模板中,反而要動態構建?有三個很好的理由:

  1. 反向解析一般比硬編碼 URL 更直觀。同時,更重要的是你能夠只在一個地方改變 URL ,而不用處處亂找。
  2. URL 建立會爲你處理特殊字符的轉義和 Unicode 數據,不用你操心。
  3. 若是你的應用是放在 URL 根路徑以外的地方(如在 /myapplication 中,不在 /中), url_for() 會爲你妥善處理。
HTTP 方法

HTTP ( web 應用使用的協議)) 協議中有訪問 URL 的不一樣方法。缺省狀況下,一個路由 只回應 GET 請求,可是能夠經過 methods 參數使用不一樣方法。例如:

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

若是當前使用的是 GET 方法,會自動添加 HEAD ,你沒必要親自操刀。同時還會確保HEAD 請求按照 HTTP RFC (說明 HTTP 協議的文檔)的要求來處理,所以你能夠 徹底忽略這部分 HTTP 規範。與 Flask 0.6 同樣, OPTIONS 自動爲你處理好。

徹底不懂 HTTP 方法?不要緊,這裏給你速成培訓一下:

HTTP 方法(一般也被稱爲「動做」)告訴服務器一個頁面請求要  什麼。如下是常見 的方法:

GET
瀏覽器告訴服務器只要  獲得 頁面上的信息併發送這些信息。這多是最多見的 方法。
HEAD
瀏覽器告訴服務器想要獲得信息,可是隻要獲得  信息頭 就好了,頁面內容不要。 一個應用應該像接受到一個  GET 請求同樣運行,可是不傳遞實際的內容。在 Flask 中,你根本沒必要理會這個,下層的 Werkzeug 庫會爲你處理好。
POST
瀏覽器告訴服務器想要向 URL  發表 一些新的信息,服務器必須確保數據被保存好 且只保存了一次。 HTML 表單實際上就是使用這個訪求向服務器傳送數據的。
PUT
與  POST 方法相似,不一樣的是服務器可能觸發屢次儲存過程而把舊的值覆蓋掉。你 可能會問這樣作有什麼用?這樣作是有緣由的。假設在傳輸過程當中鏈接丟失的狀況 下,一個處於瀏覽器和服務器之間的系統能夠在不中斷的狀況下安全地接收第二次 請求。在這種狀況下,使用  POST 方法就沒法作到了,由於它只被觸發一次。
DELETE
刪除給定位置的信息。
OPTIONS
爲客戶端提供一個查詢 URL 支持哪些方法的捷徑。從 Flask 0.6 開始,自動爲你 實現了這個方法。

有趣的是在 HTML4 和 XHTML1 中,表單只能使用 GET 和 POST 方法。可是 JavaScript 和將來的 HTML 標準中能夠使用其餘的方法。此外, HTTP 近來已經變得至關 流行,瀏覽器再也不只是惟一使用 HTTP 的客戶端。好比許多版本控制系統也使用 HTTP 。

靜態文件

動態的 web 應用也須要靜態文件,通常是 CSS 和 JavaScript 文件。理想狀況下你的 服務器已經配置好了爲你的提供靜態文件的服務。在開發過程當中, Flask 也能作好這個 工做。只要在你的包或模塊旁邊建立一個名爲 static 的文件夾就好了。靜態文件位於 應用的 /static 中。

使用選定的 'static' 端點就能夠生成相應的 URL 。:

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

這個靜態文件在文件系統中的位置應該是 static/style.css 。

渲染模板

在 Python 內部生成 HTML 很差玩,且至關笨拙。由於你必須本身負責 HTML 轉義,以 確保應用的安全。所以, Flask 自動爲你配置的 Jinja2 模板引擎。

使用 render_template() 方法能夠渲染模板,你只要提供模板名稱和須要 做爲參數傳遞給模板的變量就好了。下面是一個簡單的模板渲染例子:

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

Flask 會在 templates 文件夾內尋找模板。所以,若是你的應用是一個模塊,那麼模板 文件夾應該在模塊旁邊;若是是一個包,那麼就應該在包裏面:

情形 1: 一個模塊:

/application.py
/templates
    /hello.html

情形 2: 一個包:

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

你能夠充分使用 Jinja2 模板引擎的威力。更多內容,詳見官方 Jinja2 模板文檔 。

模板舉例:

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

在模板內部你也能夠訪問 request 、session 和 g [1] 對象,以及get_flashed_messages() 函數。

模板在繼承使用的狀況下尤爲有用,其工做原理 模板繼承 方案 文檔。簡單的說,模板繼承能夠使每一個頁面的特定元素(如頁頭,導航,頁尾)保持 一致。

自動轉義默認開啓。所以,若是 name 包含 HTML ,那麼會被自動轉義。若是你能夠 信任某個變量,且知道它是安全的 HTML (例如變量來自一個把 wiki 標記轉換爲 HTML 的模塊),那麼能夠使用 Markup 類把它標記爲安全的。不然請在模板 中使用 |safe 過濾器。更多例子參見 Jinja 2 文檔。

下面簡單介紹一下 Markup 類的工做方式:

>>> 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: 自動轉義再也不爲全部模板開啓,只爲擴展名爲 .html 、 .htm 、.xml 和 .xhtml 開啓。從字符串載入的模板將關閉自動轉義。

[1] 不理解什麼是 g 對象?它是某個能夠根據須要儲存信息的 東西。更多信息參見 g 對象的文檔和 在 Flask 中使用 SQLite 3 文檔。

操做請求數據

對於 web 應用來講對客戶端向服務器發送的數據做出響應很重要。在 Flask 中由全局 對象 request 來提供請求信息。若是你有一些 Python 基礎,那麼可能 會奇怪:既然這個對象是全局的,怎麼還能保持線程安全?答案是本地環境:

本地環境

內部信息

若是你想了解其工做原理和如何測試,請閱讀本節,不然能夠跳過本節。

某些對象在 Flask 中是全局對象,可是不是一般意義下的全局對象。這些對象其實是 特定環境下本地對象的代理。真拗口!但仍是很容易理解的。

設想如今處於處理線程的環境中。一個請求進來了,服務器決定生成一個新線程(或者 叫其餘什麼名稱的東西,這個下層的東西可以處理包括線程在內的併發系統)。當 Flask 開始其內部請求處理時會把當前線程做爲活動環境,並把當前應用和 WSGI 環境 綁定到這個環境(線程)。它以一種聰明的方式使得一個應用能夠在不中斷的狀況下 調用另外一個應用。

這對你有什麼用?基本上你能夠徹底沒必要理會。這個只有在作單元測試時纔有用。在測試 時會遇到因爲沒有請求對象而致使依賴於請求的代碼會忽然崩潰的狀況。對策是本身建立 一個請求對象並綁定到環境。最簡單的單元測試解決方案是使用test_request_context() 環境管理器。經過使用 with 語句能夠 綁定一個測試請求,以便於交互。例如:

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' 

另外一種方式是把整個 WSGI 環境傳遞給 request_context() 方法:

from flask import request with app.request_context(environ): assert request.method == 'POST' 
請求對象

請求對象在 API 一節中有詳細說明這裏不細談(參見 request )。 這裏簡略地談一下最多見的操做。首先,你必須從 flask 模塊導入請求對象:

from flask import request 

經過使用 method 屬性能夠操做當前請求方法,經過使用 form 屬性處理表單數據。如下是使用兩個屬性的例子:

@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' # 若是請求訪求是 GET 或驗證未經過就會執行下面的代碼 return render_template('login.html', error=error) 

當 form 屬性中不存在這個鍵時會發生什麼?會引起一個 KeyError 。若是你不 像捕捉一個標準錯誤同樣捕捉 KeyError ,那麼會顯示一個 HTTP 400 Bad Request 錯誤頁面。所以,多數狀況下你沒必要處理這個問題。

要操做 URL (如 ?key=value )中提交的參數能夠使用 args 屬性:

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

用戶可能會改變 URL 致使出現一個 400 請求出錯頁面,這樣下降了用戶友好度。所以, 咱們推薦使用 get 或經過捕捉 KeyError 來訪問 URL 參數。

完整的請求對象方法和屬性參見 request 文檔。

文件上傳

用 Flask 處理文件上傳很容易,只要確保不要忘記在你的 HTML 表單中設置enctype="multipart/form-data" 屬性就能夠了。不然瀏覽器將不會傳送你的文件。

已上傳的文件被儲存在內存或文件系統的臨時位置。你能夠經過請求對象 files 屬性來訪問上傳的文件。每一個上傳的文件都儲存在這個 字典型屬性中。這個屬性基本和標準 Python file 對象同樣,另外多出一個 用於把上傳文件保存到服務器的文件系統中的save() 方法。下例展現其如何運做:

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') ... 

若是想要知道文件上傳以前其在客戶端系統中的名稱,能夠使用 filename 屬性。可是請牢記這個值是 能夠僞造的,永遠不要信任這個值。若是想要把客戶端的文件名做爲服務器上的文件名, 能夠經過 Werkzeug 提供的 secure_filename() 函數:

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

更好的例子參見 上傳文件 方案。

Cookies

要訪問 cookies ,能夠使用 cookies 屬性。能夠使用請求對象 的 set_cookie 方法來設置 cookies 。請求對象的 cookies 屬性是一個包含了客戶端傳輸的全部 cookies 的字典。 在 Flask 中,若是可以使用 會話 ,那麼就不要直接使用 cookies ,由於 會話比較安全一些。

讀取 cookies:

from flask import request @app.route('/') def index(): username = request.cookies.get('username') # 使用 cookies.get(key) 來代替 cookies[key] , # 以免當 cookie 不存在時引起 KeyError 。 

儲存 cookies:

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

注意, cookies 設置在響應對象上。一般只是從視圖函數返回字符串, Flask 會把它們 轉換爲響應對象。若是你想顯式地轉換,那麼能夠使用 make_response() 函數,而後再修改它。

使用 延遲的請求回調 方案能夠在沒有響應對象的狀況下設置一個 cookie 。

同時能夠參見 關於響應 。

重定向和錯誤

使用 redirect() 函數能夠重定向。使用 abort() 能夠更早 退出請求,並返回錯誤代碼:

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

上例其實是沒有意義的,它讓一個用戶從索引頁重定向到一個沒法訪問的頁面(401 表示禁止訪問)。可是上例能夠說明重定向和出錯跳出是如何工做的。

缺省狀況下每種出錯代碼都會對應顯示一個黑白的出錯頁面。使用 errorhandler() 裝飾器能夠定製出錯頁面:

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

注意 render_template() 後面的 404 ,這表示頁面對就的出錯代碼是 404 ,即頁面不存在。缺省狀況下 200 表示一切正常。

關於響應

視圖函數的返回值會自動轉換爲一個響應對象。若是返回值是一個字符串,那麼會被轉換 爲一個包含做爲響應體的字符串、一個 200 OK 出錯代碼 和一個 text/html MIME 類型的響應對象。如下是轉換的規則:

  1. 若是視圖要返回的是一個響應對象,那麼就直接返回它。
  2. 若是要返回的是一個字符串,那麼根據這個字符串和缺省參數生成一個用於返回的 響應對象。
  3. 若是要返回的是一個元組,那麼元組中的項目能夠提供額外的信息。元組中必須至少 包含一個項目,且項目應當由 (response, status, headers) 組成。 status 的值會重載狀態代碼, headers 是一個由額外頭部值組成的列表或字典。
  4. 若是以上都不是,那麼 Flask 會假定返回值是一個有效的 WSGI 應用並把它轉換爲 一個響應對象。

若是想要在視圖內部掌控響應對象的結果,那麼能夠使用 make_response() 函數。

設想有以下視圖:

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

能夠使用 make_response() 包裹返回表達式,得到響應對象,並對該對象 進行修改,而後再返回:

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

會話

除了請求對象以外還有一種稱爲 session 的對象,容許你在不一樣請求 之間儲存信息。這個對象至關於用密鑰簽名加密的 cookie ,即用戶能夠查看你的 cookie ,可是若是沒有密鑰就沒法修改它。

使用會話以前你必須設置一個密鑰。舉例說明:

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(): # 若是會話中有用戶名就刪除它。 session.pop('username', None) return redirect(url_for('index')) # 設置密鑰,複雜一點: app.secret_key = 'A0Zr98j/3yX R~XHH!jmN]LWX/,?RT' 

這裏用到的 escape() 是用來轉義的。若是不使用模板引擎就能夠像上例 同樣使用這個函數來轉義。

如何生成一個好的密鑰

生成隨機數的關鍵在於一個好的隨機種子,所以一個好的密鑰應當有足夠的隨機性。 你的操做系統能夠使用一個隨機生成器來生成一個好的隨機種子:

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

只要複製這個隨機種子到你的代碼中就好了。

基於 cookie 的會話的說明: Flask 會把會話對象中的值儲存在 cookie 中。在打開 cookie 的狀況下,若是你訪問會話對象中沒有的值,那麼會獲得模糊的錯誤信息:請檢查 頁面 cookie 的大小是否與網絡瀏覽器所支持的大小一致。

消息閃現

一個好的應用和用戶接口都有良好的反饋,不然到後來用戶就會討厭這個應用。 Flask 經過閃現系統來提供了一個易用的反饋方式。閃現系統的基本工做原理是在請求結束時 記錄一個消息,提供且只提供給下一個請求使用。一般經過一個佈局模板來展示閃現的 消息。

flash() 用於閃現一個消息。在模板中,使用 get_flashed_messages() 來操做消息。完整的例子參見 消息閃現 。

日誌

New in version 0.3.

有時候可能會遇到數據出錯須要糾正的狀況。例如由於用戶篡改了數據或客戶端代碼出錯 而致使一個客戶端代碼向服務器發送了明顯錯誤的 HTTP 請求。多數時候在相似狀況下 返回 400 Bad Request 就沒事了,但也有不會返回的時候,而代碼還得繼續運行 下去。

這時候就須要使用日誌來記錄這些不正常的東西了。自從 Flask 0.3 後就已經爲你配置好 了一個日誌工具。

如下是一些日誌調用示例:

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

logger 是一個標準的 Python Logger 類, 更多信息詳見官方的 logging 文檔 。

集成 WSGI 中間件

若是想要在應用中添加一個 WSGI 中間件,那麼能夠包裝內部的 WSGI 應用。假設爲了 解決 lighttpd 的錯誤,你要使用一個來自 Werkzeug 包的中間件,那麼能夠這樣作:

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

部署到一個網絡服務器

準備好發佈你的新 Flask 應用了嗎?做爲本文的一個圓滿結尾,你能夠當即把應用部署到 一個主機上。下面介紹的是如何把小項目部署到免費主機上。

其餘能夠部署 Flask 應用的地方:

若是擁有本身的獨立主機,參見《 部署方式 》。

教程

想要用 Python 和 Flask 開發應用嗎?讓咱們來邊看例子邊學習。本教程中咱們將會建立 一個微博應用。這個應用只支持單一用戶,只能建立文本條目,也沒有不能訂閱和評論, 可是已經具有一個初學者須要掌握的功能。這個應用只須要使用 Flask 和 SQLite , SQLite 是 Python 自帶的。

若是你想要事先下載完整的源代碼或者用於比較,請查看 示例源代碼 。

Flaskr 介紹

咱們把教程中的博客應用稱爲 flaskr ,固然你也能夠隨便取一個沒有 web-2.0 氣息的 名字 ;) 它的基本功能以下:

  1. 讓用戶能夠根據配置文件中的信息登陸和註銷。只支持一個用戶。
  2. 用戶登陸之後能夠添加新的博客條目。條目由文本標題和支持 HTML 代碼的內容組成。 由於咱們信任用戶,因此不對內容中的 HTML 進行淨化處理。
  3. 頁面以倒序(新的在上面)顯示全部條目。而且用戶登陸之後能夠在這個頁面添加新 的條目。

咱們直接在應用中使用 SQLite3 ,由於在這種規模的應用中 SQlite3 已經夠用了。若是 是大型應用,那麼就有必要使用可以好的處理數據庫鏈接的 SQLAlchemy 了,它能夠 同時對應多種數據庫,並作其餘更多的事情。若是你的數據更適合使用 NoSQL 數據庫, 那麼也能夠考慮使用某種流行的 NoSQL 數據庫。

這是教程應用完工後的截圖:

screenshot of the final application

下面請閱讀 步驟 0 :建立文件夾 。

步驟 0 :建立文件夾

在開始以前須要爲應用建立下列文件夾:

/flaskr
    /static
    /templates

flaskr 文件夾不是一個 Python 包,只是一個用來存放咱們文件的地方。咱們將把之後 要用到的數據庫模式和主模塊放在這個文件夾中。 static 文件夾中的文件是用於供 應用用戶經過 HTTP 訪問的文件,主要是 CSS 和 javascript 文件。 Flask 將會在 templates 文件夾中搜索 Jinja2 模板,全部在教程中的模板都放在 templates 文件夾中。

下面請閱讀 步驟 1 :數據庫模式 。

步驟 1 :數據庫模式

首先咱們要建立數據庫模式。本應用只須要使用一張表,而且因爲咱們使用 SQLite , 因此這一步很是簡單。把如下內容保存爲 schema.sql 文件並放在咱們上一步建立的 flaskr文件夾中就好了:

drop table if exists entries; create table entries ( id integer primary key autoincrement, title text not null, text text not null ); 

這個模式只有一張名爲 entries 的表,表中的字段爲 id 、 title 和 text 。 id 是主鍵,是自增整數型字段,另外兩個字段是非空的字符串型字段。

下面請閱讀 步驟 2 :應用構建代碼 。

步驟 2 :應用構建代碼

如今咱們已經準備好了數據庫模式了,下面來建立應用模塊。咱們把模塊命名爲flaskr.py ,並放在 flaskr 文件夾中。爲了方便初學者學習,咱們把庫的導入與 相關配置放在了一塊兒。對於小型應用來講,能夠把配置直接放在模塊中。可是更加清晰的 方案是把配置放在一個獨立的 .ini 或 .py 文件中,並在模塊中導入配置的值。

在 flaskr.py 文件中:

# all the imports
import sqlite3 from flask import Flask, request, session, g, redirect, url_for, \ abort, render_template, flash # configuration DATABASE = '/tmp/flaskr.db' DEBUG = True SECRET_KEY = 'development key' USERNAME = 'admin' PASSWORD = 'default' 

接着建立真正的應用,並用同一文件中的配置來初始化,在 flaskr.py 文件中:

# create our little application :)
app = Flask(__name__) app.config.from_object(__name__) 

from_object() 會查看給定的對象(若是該對象是一個字符串就會 直接導入它),搜索對象中全部變量名均爲大字字母的變量。在咱們的應用中,已經將配 置寫在前面了。你能夠把這些配置放到一個獨立的文件中。

一般,從一個配置文件中導入配置是比較好的作法,咱們使用 from_envvar() 來完成這個工做,把上面的 from_object() 一行替換爲:

app.config.from_envvar('FLASKR_SETTINGS', silent=True) 

這樣作就能夠設置一個 FLASKR_SETTINGS 的環境變量來指定一個配置文件,並 根據該文件來重載缺省的配置。 silent 開關的做用是告訴 Flask 若是沒有這個環境變量 不要報錯。

secret_key (密鑰)用於保持客戶端會話安全,請謹慎地選擇密鑰,並儘量的使它 複雜並且不容易被猜到。 DEBUG 標誌用於開關交互調試器。由於調試模式容許用戶執行 服務器上的代碼,因此 永遠不要在生產環境中打開調試模式 !

咱們還添加了一個方便鏈接指定數據庫的方法。這個方法能夠用於在請求時打開鏈接,也 能夠用於 Python 交互終端或代碼中。之後會派上用場。

def connect_db(): return sqlite3.connect(app.config['DATABASE']) 

最後,在文件末尾添加以單機方式啓動服務器的代碼:

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

到此爲止,咱們能夠順利運行應用了。輸入如下命令開始運行:

python flaskr.py

你會看到服務器已經運行的信息,其中包含應用訪問地址。

由於咱們還沒建立視圖,因此當你在瀏覽器中訪問這個地址時,會獲得一個 404 頁面未 找到錯誤。很快咱們就會談到視圖,但咱們先要弄好數據庫。

外部可見的服務器

想讓你的服務器被公開訪問?詳見 外部可見的服務器 。

下面請閱讀 步驟 3 :建立數據庫 。

步驟 3 :建立數據庫

如前所述 Flaskr 是一個數據庫驅動的應用,更準確地說是一個關係型數據庫驅動的 應用。關係型數據庫須要一個數據庫模式來定義如何儲存信息,所以必須在第一次運行 服務器前建立數據庫模式。

使用 sqlite3 命令經過管道導入 schema.sql 建立模式:

sqlite3 /tmp/flaskr.db < schema.sql 

上述方法的不足之處是須要額外的 sqlite3 命令,但這個命令不是每一個系統都有的。並且還必須提供數據庫的路徑,容易出錯。所以更好的方法是在應用中添加一個數據庫初始化 函數。

添加的方法是:首先從 contextlib 庫中導入 contextlib.closing() 函數,即在flaskr.py 文件的導入部分添加以下內容:

from contextlib import closing 

接下來,能夠建立一個用來初始化數據庫的 init_db 函數,其中咱們使用了先前建立的connect_db 函數。把這個初始化函數放在 flaskr.py 文件中的`connect_db` 函數 下面:

def init_db(): with closing(connect_db()) as db: with app.open_resource('schema.sql', mode='r') as f: db.cursor().executescript(f.read()) db.commit() 

closing() 幫助函數容許咱們在 with 代碼塊保持數據庫鏈接 打開。應用對象的open_resource() 方法支持也支持這個功能, 能夠在 with 代碼塊中直接使用。這個函數打開一個位於來源位置(你的 flaskr 文件夾)的文件並容許你讀取文件的內容。這裏咱們用於在數據庫鏈接上執行 代碼。

當咱們鏈接到數據庫時,咱們獲得一個提供指針的鏈接對象(本例中的 db )。這個 指針有一個方法能夠執行完整的代碼。最後咱們提供要作的修改。 SQLite 3 和其餘 事務型數據庫只有在顯式提交時纔會真正提交。

如今能夠建立數據庫了。打開 Python shell ,導入,調用函數:

>>> from flaskr import init_db >>> init_db() 

故障處理

若是出現表沒法找到的問題,請檢查是否寫錯了函數名稱(應該是 init_db ), 是否寫錯了表名(例如單數複數錯誤)。

下面請閱讀 步驟 4 :請求數據庫鏈接 。

步驟 4 :請求數據庫鏈接

如今咱們已經學會如何打開並在代碼中使用數據庫鏈接,可是如何優雅地在請求時使用它 呢?咱們會在每個函數中用到數據庫鏈接,所以有必要在請求以前初始化鏈接,並在 請求以後關閉鏈接。

Flask 中能夠使用 before_request() 、 after_request() 和teardown_request() 裝飾器達到這個目的:

@app.before_request
def before_request(): g.db = connect_db() @app.teardown_request def teardown_request(exception): db = getattr(g, 'db', None) if db is not None: db.close() 

使用 before_request() 裝飾的函數會在請求以前調用,且不傳遞 參數。使用after_request() 裝飾的函數會在請求以後調用,且 傳遞發送給客戶端響應對象。它們必須傳遞響應對象,因此在出錯的狀況下就不會執行。 所以咱們就要用到teardown_request() 裝飾器了。這個裝飾器下 的函數在響應對象構建後被調用。它們不容許修改請求,而且它們的返回值被忽略。若是 請求過程當中出錯,那麼這個錯誤會傳遞給每一個函數;不然傳遞 None 。

咱們把數據庫鏈接保存在 Flask 提供的特殊的 g 對象中。這個對象與 每個請求是一一對應的,而且只在函數內部有效。不要在其它對象中儲存相似信息, 由於在多線程環境下無效。這個特殊的 g 對象會在後臺神奇的工做,保 證系統正常運行。

若想更好地處理這種資源,請參閱 在 Flask 中使用 SQLite 3 。

下面請閱讀 步驟 5 :視圖函數.

Hint

我該把這些代碼放在哪裏?

若是你按教程一步一步讀下來,那麼可能會疑惑應該把這個步驟和之後的代碼放在哪 裏?比較有條理的作法是把這些模塊級別的函數放在一塊兒,並把新的 before_request和 teardown_request 函數放在前文的 init_db 函數 下面(即按照教程的順序放置)。

若是你已經暈頭轉向了,那麼你能夠參考一下 示例源代碼 。在 Flask 中,你能夠 把應用的全部代碼都放在同一個 Python 模塊中。可是你沒有必要這樣作,尤爲是當你 的應用變大了 的時候,更不該當這樣。

步驟 5 :視圖函數

如今數據庫鏈接弄好了,接着開始寫視圖函數。咱們共須要四個視圖函數:

顯示條目

這個視圖顯示全部數據庫中的條目。它綁定應用的根地址,並從數據庫中讀取 title 和 text 字段。 id 最大的記錄(最新的條目)在最上面。從指針返回的記錄集是一個包含 select 語句查詢結果的元組。對於教程應用這樣的小應用,作到這樣就已經夠好了。可是 你可能想要把結果轉換爲字典,具體作法參見 簡化查詢 中的例子。

這個視圖會把條目做爲字典傳遞給 show_entries.html 模板,並返回渲染結果:

@app.route('/') def show_entries(): cur = g.db.execute('select title, text from entries order by id desc') entries = [dict(title=row[0], text=row[1]) for row in cur.fetchall()] return render_template('show_entries.html', entries=entries) 
添加一個新條目

這個視圖可讓一個登陸後的用戶添加一個新條目。本視圖只響應 POST 請求,真正的 表單顯示在 show_entries 頁面中。若是一切順利,咱們會 flash() 一個消息給下一個請求並重定向回到 show_entries 頁面:

@app.route('/add', methods=['POST']) def add_entry(): if not session.get('logged_in'): abort(401) g.db.execute('insert into entries (title, text) values (?, ?)', [request.form['title'], request.form['text']]) g.db.commit() flash('New entry was successfully posted') return redirect(url_for('show_entries')) 

注意,咱們在本視圖中檢查了用戶是否已經登陸(即檢查會話中是否有 logged_in 鍵,且 對應的值是否爲 True )。

安全性建議

請像示例代碼同樣確保在構建 SQL 語句時使用問號。不然當你使用字符串構建 SQL 時 容易遭到 SQL 注入攻擊。更多內容參見 在 Flask 中使用 SQLite 3 。

登陸和註銷

這些函數用於用戶登陸和註銷。登陸視圖根據配置中的用戶名和密碼驗證用戶並在會話中 設置 logged_in 鍵值。若是用戶經過驗證,鍵值設爲 True ,那麼用戶會被重定向到show_entries 頁面。另外閃現一個信息,告訴用戶已登陸成功。若是出現錯誤,模板會 提示錯誤信息,並讓用戶從新登陸:

@app.route('/login', methods=['GET', 'POST']) def login(): error = None if request.method == 'POST': if request.form['username'] != app.config['USERNAME']: error = 'Invalid username' elif request.form['password'] != app.config['PASSWORD']: error = 'Invalid password' else: session['logged_in'] = True flash('You were logged in') return redirect(url_for('show_entries')) return render_template('login.html', error=error) 

登出視圖則正好相反,把鍵值從會話中刪除。在這裏咱們使用了一個小技巧:若是你使用 字典的 pop() 方法而且傳遞了第二個參數(鍵的缺省值),那麼當字典中有 這個鍵時就會刪除這個鍵,不然什麼也不作。這樣作的好處是咱們不用檢查用戶是否已經 登陸了。

@app.route('/logout') def logout(): session.pop('logged_in', None) flash('You were logged out') return redirect(url_for('show_entries')) 

下面請閱讀 步驟 6 :模板 。

步驟 6 :模板

如今開始寫模板。若是咱們如今訪問 URL ,那麼會獲得一個 Flask 沒法找到模板文件的 異常。 Flask 使用 Jinja2 模板語法並默認開啓自動轉義。也就是說除非用 Markup 標記一個值或在模板中使用 |safe 過濾器,不然 Jinja2 會把如 < 或 > 之類的特殊字符轉義爲與其 XML 等價字符。

咱們還使用了模板繼承以保存全部頁面的佈局統一。

請把如下模板放在 templates 文件夾中:

layout.html

這個模板包含 HTML 骨架、頭部和一個登陸連接(若是用戶已登陸則變爲一個註銷連接 )。若是有閃現信息,那麼還會顯示閃現信息。 {% block body %} 塊會被子模板中 同名( body )的塊替換。

session 字典在模板中也能夠使用。你能夠使用它來檢驗用戶是否已經 登陸。注意,在 Jinja 中能夠訪問對象或字典的不存在的屬性和成員。如例子中的 'logged_in' 鍵不存在時代碼仍然能正常運行:

<!doctype html>
<title>Flaskr</title> <link rel=stylesheet type=text/css href="{{ url_for('static', filename='style.css') }}"> <div class=page> <h1>Flaskr</h1> <div class=metanav> {% if not session.logged_in %} <a href="{{ url_for('login') }}">log in</a> {% else %} <a href="{{ url_for('logout') }}">log out</a> {% endif %} </div> {% for message in get_flashed_messages() %} <div class=flash>{{ message }}</div> {% endfor %} {% block body %}{% endblock %} </div> 
show_entries.html

這個模板擴展了上述的 layout.html 模板,用於顯示信息。注意, for 遍歷了咱們 經過render_template() 函數傳遞的全部信息。模板還告訴表單使用 POST 做爲 HTTP 方法向 add_entry 函數提交數據:

{% extends "layout.html" %} {% block body %} {% if session.logged_in %} <form action="{{ url_for('add_entry') }}" method=post class=add-entry> <dl> <dt>Title: <dd><input type=text size=30 name=title> <dt>Text: <dd><textarea name=text rows=5 cols=40></textarea> <dd><input type=submit value=Share> </dl> </form> {% endif %} <ul class=entries> {% for entry in entries %} <li><h2>{{ entry.title }}</h2>{{ entry.text|safe }} {% else %} <li><em>Unbelievable. No entries here so far</em> {% endfor %} </ul> {% endblock %} 
login.html

最後是簡單顯示用戶登陸表單的登陸模板:

{% extends "layout.html" %} {% block body %} <h2>Login</h2> {% if error %}<p class=error><strong>Error:</strong> {{ error }}{% endif %} <form action="{{ url_for('login') }}" method=post> <dl> <dt>Username: <dd><input type=text name=username> <dt>Password: <dd><input type=password name=password> <dd><input type=submit value=Login> </dl> </form> {% endblock %} 

下面請閱讀 步驟 7 :添加樣式 。

步驟 7 :添加樣式

如今萬事俱備,只剩給應用添加一些樣式了。只要把如下內容保存爲 static 文件夾中 的style.css 文件就好了:

body            { font-family: sans-serif; background: #eee; } a, h1, h2 { color: #377ba8; } h1, h2 { font-family: 'Georgia', serif; margin: 0; } h1 { border-bottom: 2px solid #eee; } h2 { font-size: 1.2em; } .page { margin: 2em auto; width: 35em; border: 5px solid #ccc; padding: 0.8em; background: white; } .entries { list-style: none; margin: 0; padding: 0; } .entries li { margin: 0.8em 1.2em; } .entries li h2 { margin-left: -1em; } .add-entry { font-size: 0.9em; border-bottom: 1px solid #ccc; } .add-entry dl { font-weight: bold; } .metanav { text-align: right; font-size: 0.8em; padding: 0.3em; margin-bottom: 1em; background: #fafafa; } .flash { background: #cee5F5; padding: 0.5em; border: 1px solid #aacbe2; } .error { background: #f0d6d6; padding: 0.5em; } 

下面請閱讀 額外贈送:測試應用 。

額外贈送:測試應用

如今你已經完成了整個應用,一切都運行正常。爲了方便之後進行完善修改,添加自動 測試不失爲一個好主意。本教程中的應用已成爲 測試 Flask 應用 文檔中演示如何進行 單元測試的例子,能夠去看看測試 Flask 應用是多麼容易。

模板

Flask 使用 Jinja2 做爲默認模板引擎。你徹底能夠使用其它模板引擎。可是無論你使用 哪一種模板引擎,都必須安裝 Jinja2 。由於使用 Jinja2 可讓 Flask 使用更多依賴於 這個模板引擎的擴展。

本文只是簡單介紹如何在 Flask 中使用 Jinja2 。若是要詳細瞭解這個模板引擎的語法, 請查閱 Jinja2 模板官方文檔 。

Jinja 設置

在 Flask 中, Jinja2 默認配置以下:

  • 在擴展名爲 .html 、 .htm 、 .xml 和 .xhtml 的模板中開啓自動 轉義。
  • 在模板中能夠使用 {% autoescape %} 來手動設置是否轉義。
  • Flask 在 Jinja2 環境中加入一些全局函數和輔助對象,以加強模板的功能。

標準環境

缺省狀況下,如下全局變量能夠在 Jinja2 模板中使用:

config

當前配置對象 ( flask.config )

New in version 0.6.

Changed in version 0.10: 此版本開始,這個變量老是可用,甚至是在被導入的模板中。

request

當前請求對象 ( flask.request )。在沒有活動請求環境狀況下渲染模塊 時,這個變量不可用。

session

當前會話對象 ( flask.session )。在沒有活動請求環境狀況下渲染模塊 時,這個變量不可用。

g

請求綁定的全局變量 ( flask.g )。在沒有活動請求環境狀況下渲染模塊 時,這個變量不可用。

url_for ()

flask.url_for() 函數。

get_flashed_messages ()

flask.get_flashed_messages() 函數。

Jinja 環境行爲

這些添加到環境中的變量不是全局變量。與真正的全局變量不一樣的是這些變量在已導入 的模板的環境中是不可見的。這樣作是基於性能的緣由,同時也考慮讓代碼更有條理。

那麼對你來講又有什麼意義呢?假設你須要導入一個宏,這個宏須要訪問請求對象, 那麼你有兩個選擇:

  1. 顯式地把請求或都該請求有用的屬性做爲參數傳遞給宏。
  2. 導入 「with context」 宏。

導入方式以下:

{% from '_helpers.html' import my_macro with context %}

標準過濾器

在 Flask 中的模板中添加了如下 Jinja2 自己沒有的過濾器:

tojson ()

這個函數能夠把對象轉換爲 JSON 格式。若是你要動態生成 JavaScript ,那麼這個 函數很是有用。

注意,在 script 標記內部不能轉義,所以在 Flask 0.10 以前的版本中,若是要在script 標記內部使用這個函數必須用 |safe 關閉轉義:

<script type=text/javascript> doSomethingWith({{ user.username|tojson|safe }}); </script> 

控制自動轉義

自動轉義是指自動對特殊字符進行轉義。特殊字符是指 HTML ( 或 XML 和 XHTML )中的 & 、 > 、 < 、 " 和 ' 。由於這些特殊字符表明了特殊的意思, 因此若是要在文本中使用它們就必須把它們替換爲「實體」。若是不轉義,那麼用戶就 沒法使用這些字符,並且還會帶來安全問題。(參見 跨站腳本攻擊(XSS) )

有時候,如須要直接把 HTML 植入頁面的時候,可能會須要在模板中關閉自動轉義功能。 這個能夠直接植入的 HTML 通常來自安全的來源,例如一個把標記語言轉換爲 HTML 的 轉換器。

有三種方法能夠控制自動轉義:

  • 在 Python 代碼中,能夠在把 HTML 字符串傳遞給模板以前,用 Markup 對象封裝。通常狀況下推薦使用這個方法。
  • 在模板中,使用 |safe 過濾器顯式把一個字符串標記爲安全的 HTML (例如: {{myvariable|safe }} )。
  • 臨時關閉整個系統的自動轉義。

在模板中關閉自動轉義系統能夠使用 {% autoescape %} 塊:

{% autoescape false %} <p>autoescaping is disabled here <p>{{ will_not_be_escaped }} {% endautoescape %} 

在這樣作的時候,要很是當心塊中的變量的安全性。

註冊過濾器

有兩種方法能夠在 Jinja2 中註冊你本身的過濾器。要麼手動把它們放入應用的jinja_env 中,要麼使用 template_filter() 裝飾器。

下面兩個例子功能相同,都是倒序一個對象:

@app.template_filter('reverse') def reverse_filter(s): return s[::-1] def reverse_filter(s): return s[::-1] app.jinja_env.filters['reverse'] = reverse_filter 

裝飾器的參數是可選的,若是不給出就使用函數名做爲過濾器名。一旦註冊完成後,你就 能夠在模板中像 Jinja2 的內建過濾器同樣使用過濾器了。例如,假設在環境中你有一個 名爲 mylist 的 Pyhton 列表:

{% for x in mylist | reverse %}
{% endfor %}

環境處理器

環境處理器的做用是把新的變量自動引入模板環境中。環境處理器在模板被渲染前運行, 所以能夠把新的變量自動引入模板環境中。它是一個函數,返回值是一個字典。在應用的 全部模板中,這個字典將與模板環境合併:

@app.context_processor
def inject_user(): return dict(user=g.user) 

上例中的環境處理器建立了一個值爲 g.user 的 user 變量,並把這個變量加入了 模板環境中。這個例子只是用於說明工做原理,不是很是有用,由於在模板中, g 老是 存在的。

傳遞值不只僅侷限於變量,還能夠傳遞函數( Python 提供傳遞函數的功能):

@app.context_processor
def utility_processor(): def format_price(amount, currency=u'€'): return u'{0:.2f}{1}'.format(amount, currency) return dict(format_price=format_price) 

上例中的環境處理器把 format_price 函數傳遞給了全部模板:

{{ format_price(0.33) }} 

你還能夠把 format_price 建立爲一個模板過濾器(參見 註冊過濾器 ),這裏只是演示如何在一個環境處理器中傳遞函數。

測試 Flask 應用

未經測試的小貓,確定不是一隻好貓。

這句話的出處不詳(譯者注:這句是譯者獻給小貓的),也不必定徹底正確,可是基本上 是正確的。未經測試的應用難於改進現有的代碼,所以其開發者會越改進越抓狂。反之, 通過自動測試的代碼能夠安全的改進,而且若是能夠測試過程當中當即發現錯誤。

Flask 提供的測試渠道是公開 Werkzeug 的 Client ,爲你 處理本地環境。你能夠結合這個渠道使用你喜歡的測試工具。本文使用的測試工具是隨着 Python 一塊兒安裝好的unittest 包。

應用

首先,咱們須要一個用來測試的應用。咱們將使用 教程 中的應用。若是你還 沒有這個應用,能夠下載 示例代碼 。

測試骨架

爲了測試應用,咱們添加了一個新的模塊 (flaskr_tests.py) 並建立了以下測試骨架:

import os import flaskr import unittest import tempfile class FlaskrTestCase(unittest.TestCase): def setUp(self): self.db_fd, flaskr.app.config['DATABASE'] = tempfile.mkstemp() flaskr.app.config['TESTING'] = True self.app = flaskr.app.test_client() flaskr.init_db() def tearDown(self): os.close(self.db_fd) os.unlink(flaskr.app.config['DATABASE']) if __name__ == '__main__': unittest.main() 

setUp() 方法中會建立一個新的測試客戶端並初始化一個新的 數據庫。在每一個獨立的測試函數運行前都會調用這個方法。 tearDown() 方法的功能是在測試結束後關閉文件,並在文件 系統中刪除數據庫文件。另外在設置中 TESTING 標誌開啓的,這意味着在請求時關閉 錯誤捕捉,以便於在執行測試請求時獲得更好的錯誤報告。

測試客戶端會給咱們提供一個簡單的應用接口。咱們能夠經過這個接口嚮應用發送測試 請求。客戶端還能夠追蹤 cookies 。

由於 SQLite3 是基於文件系統的,因此咱們能夠方便地使用臨時文件模塊來建立一個臨時 數據庫並初始化它。 mkstemp() 函數返回兩個東西:一個低級別的文件 句柄和一個隨機文件名。這個文件名後面將做爲咱們的數據庫名稱。咱們必須把句柄保存 到 db_fd中,以便於之後用 os.close() 函數來關閉文件。

若是如今進行測試,那麼會輸出如下內容:

$ python flaskr_tests.py

----------------------------------------------------------------------
Ran 0 tests in 0.000s

OK

雖然沒有運行任何實際測試,可是已經能夠知道咱們的 flaskr 應用沒有語法錯誤。 不然在導入時會引起異常並中斷運行。

第一個測試

如今開始測試應用的功能。當咱們訪問應用的根 URL ( / )時應該顯示 「 No entries here so far 」。咱們新增了一個新的測試方法來測試這個功能:

class FlaskrTestCase(unittest.TestCase): def setUp(self): self.db_fd, flaskr.app.config['DATABASE'] = tempfile.mkstemp() self.app = flaskr.app.test_client() flaskr.init_db() def tearDown(self): os.close(self.db_fd) os.unlink(flaskr.app.config['DATABASE']) def test_empty_db(self): rv = self.app.get('/') assert 'No entries here so far' in rv.data 

注意,咱們的調試函數都是以 test 開頭的。這樣 unittest 就會自動識別這些 是用於測試的函數並運行它們。

經過使用 self.app.get ,能夠嚮應用的指定 URL 發送 HTTP GET 請求,其返回的是 一個 ~flask.Flask.response_class 對象。咱們能夠使用 data 屬性來檢查應用的返回值(字符串 類型)。在本例中,咱們檢查輸出是否包含 'No entries here so far' 。

再次運行測試,會看到經過了一個測試:

$ python flaskr_tests.py
.
----------------------------------------------------------------------
Ran 1 test in 0.034s

OK

登陸和註銷

咱們應用的主要功能必須登陸之後才能使用,所以必須測試應用的登陸和註銷。測試的 方法是使用規定的數據(用戶名和密碼)嚮應用發出登陸和註銷的請求。由於登陸和註銷 後會重定向到別的頁面,所以必須告訴客戶端使用 follow_redirects 追蹤重定向。

在 FlaskrTestCase 類中添加如下兩個方法:

def login(self, username, password): return self.app.post('/login', data=dict( username=username, password=password ), follow_redirects=True) def logout(self): return self.app.get('/logout', follow_redirects=True) 

如今能夠方便地測試登陸成功、登陸失敗和註銷功能了。下面爲新增的測試代碼:

def test_login_logout(self): rv = self.login('admin', 'default') assert 'You were logged in' in rv.data rv = self.logout() assert 'You were logged out' in rv.data rv = self.login('adminx', 'default') assert 'Invalid username' in rv.data rv = self.login('admin', 'defaultx') assert 'Invalid password' in rv.data 

測試增長條目功能

咱們還要測試增長條目功能。添加如下測試代碼:

def test_messages(self): self.login('admin', 'default') rv = self.app.post('/add', data=dict( title='<Hello>', text='<strong>HTML</strong> allowed here' ), follow_redirects=True) assert 'No entries here so far' not in rv.data assert '&lt;Hello&gt;' in rv.data assert '<strong>HTML</strong> allowed here' in rv.data 

這裏咱們檢查了博客內容中容許使用 HTML ,但標題不能夠。應用設計思路就是這樣的。

運行測試,如今經過了三個測試:

$ python flaskr_tests.py
...
----------------------------------------------------------------------
Ran 3 tests in 0.332s

OK

關於更復雜的 HTTP 頭部和狀態碼測試參見 MiniTwit 示例 。這個示例的源代碼中 包含了更大的測試套件。

其餘測試技巧

除了使用上述測試客戶端外,還能夠在 with 語句中使用 test_request_context()方法來臨時激活一個請求環境。在這個 環境中能夠像以視圖函數中同樣操做 requestg 和 session 對象。示例:

app = flask.Flask(__name__) with app.test_request_context('/?name=Peter'): assert flask.request.path == '/' assert flask.request.args['name'] == 'Peter' 

其餘與環境綁定的對象也能夠這樣使用。

若是你必須使用不一樣的配置來測試應用,並且沒有什麼好的測試方法,那麼能夠考慮使用 應用工廠(參見 應用工廠 )。

注意,在測試請求環境中 before_request() 函數和 after_request() 函數不會被自動調用。可是當調試請求環境離開 with 塊時會執行 teardown_request() 函數。若是須要 before_request() 函數和正常狀況下同樣被調用,那麼你必須調用preprocess_request()

app = flask.Flask(__name__) with app.test_request_context('/?name=Peter'): app.preprocess_request() ... 

在這函數中能夠打開數據庫鏈接或者根據應用須要打開其餘相似東西。

若是想調用 after_request() 函數,那麼必須調用 process_response() ,並把響應對象傳遞給它:

app = flask.Flask(__name__) with app.test_request_context('/?name=Peter'): resp = Response('...') resp = app.process_response(resp) ... 

這個例子中的狀況基本沒有用處,由於在這種狀況下能夠直接開始使用測試客戶端。

僞造資源和環境

New in version 0.10.

一般狀況下,咱們會把用戶認證信息和數據庫鏈接儲存到應用環境或者 flask.g 對象中,並在第一次使用前準備好,而後在斷開時刪除。假設應用中 獲得當前用戶的代碼以下:

def get_user(): user = getattr(g, 'user', None) if user is None: user = fetch_current_user_from_database() g.user = user return user 

在測試時能夠很很方便地重載用戶而不用改動代碼。能夠先象下面這樣鉤接flask.appcontext_pushed 信號:

from contextlib import contextmanager from flask import appcontext_pushed @contextmanager def user_set(app, user): def handler(sender, **kwargs): g.user = user with appcontext_pushed.connected_to(handler, app): yield 

而後使用:

from flask import json, jsonify @app.route('/users/me') def users_me(): return jsonify(username=g.user.username) with user_set(app, my_user): with app.test_client() as c: resp = c.get('/users/me') data = json.loads(resp.data) self.assert_equal(data['username'], my_user.username) 

保持環境

New in version 0.4.

有時候這種情形是有用的:觸發一個常規請求,可是保持環境以便於作一點額外 的事情。 在 Flask 0.4 以後能夠在 with 語句中使用 test_client() 來 實現:

app = flask.Flask(__name__) with app.test_client() as c: rv = c.get('/?tequila=42') assert request.args['tequila'] == '42' 

若是你在沒有 with 的狀況下使用 test_client() ,那麼 assert 會出錯失敗。由於沒法在請求以外訪問 request 。

訪問和修改會話

New in version 0.8.

有時候在測試客戶端中訪問和修改會話是很是有用的。一般有兩方法。若是你想測試會話中 的鍵和值是否正確,你能夠使用 flask.session:

with app.test_client() as c: rv = c.get('/') assert flask.session['foo'] == 42 

可是這個方法沒法修改會話或在請求發出前訪問會話。自 Flask 0.8 開始,咱們提供了 「會話處理」,用打開測試環境中會話和修改會話,最後保存會話。處理後的會話獨立於 後端實際使用的會話:

with app.test_client() as c: with c.session_transaction() as sess: sess['a_key'] = 'a value' # 運行到這裏時,會話已被保存 

注意在這種狀況下必須使用 sess 對象來代替 flask.session 代理。 sess 對象自己能夠提供相同的接口。

掌握應用錯誤

New in version 0.3.

應用出錯,服務器出錯。或早或晚,你會遇到產品出錯。即便你的代碼是百分百正確, 仍是會時常看見出錯。爲何?由於其餘相關東西會出錯。如下是一些在代碼徹底正確的 條件下服務器出錯的狀況:

  • 客戶端已經中斷了請求,但應用還在讀取數據。
  • 數據庫已通過載,沒法處理查詢。
  • 文件系統沒有空間。
  • 硬盤完蛋了。
  • 後臺服務過載。
  • 使用的庫出現程序錯誤。
  • 服務器與另外一個系統的網絡鏈接出錯。

以上只是你會遇到的問題的一小部分。那麼若是處理這些問題呢?若是你的應用運行在 生產環境下,那麼缺省狀況下 Flask 會顯示一個簡單的出錯頁面,並把出錯狀況記錄到logger 。

但要作得還不僅這些,下面介紹一些更好的出錯處理方法。

報錯郵件

若是應用在生產環境(在你的服務器中通常使用生產環境)下運行,那麼缺省狀況下不會 看到任何日誌信息。爲何?由於 Flask 是一個零配置的框架。既然沒有配置,那麼日誌 放在哪裏呢?顯然, Flask 不能來隨便找一個地放給用戶存放日誌,由於若是用戶在這個 位置沒有建立文件的權限就糟了。同時,對於大多數小應用來講,沒人會去看日誌。

事實上,我如今能夠負責任地說除非調試一個用戶向你報告的錯誤,你是不會去看應用的 日誌文件的。你真下須要的是出錯的時候立刻給你發封電子郵件,及時提醒你,以便於 進行處理。

Flask 使用 Python 內置的日誌系統,它能夠發送你須要的錯誤報告電子郵件。如下是 如何配置 Flask 日誌記錄器發送錯誤報告電子郵件的例子:

ADMINS = ['yourname@example.com'] if not app.debug: import logging from logging.handlers import SMTPHandler mail_handler = SMTPHandler('127.0.0.1', 'server-error@example.com', ADMINS, 'YourApplication Failed') mail_handler.setLevel(logging.ERROR) app.logger.addHandler(mail_handler) 

這個例子是什麼意思?咱們建立了一個新的 SMTPHandler 類。這個類會使用郵件服務器 127.0.0.1 向 server-error@example.com 的 ADMINS 發送主題爲 「YourApplication Failed」 的 電子郵件。若是你的郵件服務器 須要認證,這是可行的,詳見 SMTPHandler的文檔。

咱們還定義了只報送錯誤及錯誤以上級別的信息。由於咱們不想獲得警告或其餘沒用的 日誌,好比請求處理日誌。

在你的產品中使用它們前,請查看一下 控制日誌格式 ,以瞭解錯誤報告郵件的更多 信息,磨刀不誤砍柴功。

日誌文件

報錯郵件有了,可能還須要記錄警告信息。這是一個好習慣,有利於除錯。請注意,在 核心系統中 Flask 自己不會發出任何警告。所以,在有問題時發出警告只能自力更生了。

雖然有許多日誌記錄系統,但不是每一個系統都能作好基本日誌記錄的。如下多是最值得 關注的:

  • FileHandler - 把日誌信息記錄到文件系統中的一個文件。
  • RotatingFileHandler - 把日誌信息記錄到文件系統中 的一個文件,當信息達到必定數量後反轉。
  • NTEventLogHandler - 把日誌信息記錄到 Windows 的 事件日誌中。若是你的應用部署在 Windows 下,就用這個吧。
  • SysLogHandler - 把日誌記錄到一個 UNIX 系統日誌。

一旦你選定了日誌記錄器以後,使用方法相似上一節所講的 SMTP 處理器,只是記錄的 級別應當低一點(我推薦 WARNING 級別):

if not app.debug: import logging from themodule import TheHandlerYouWant file_handler = TheHandlerYouWant(...) file_handler.setLevel(logging.WARNING) app.logger.addHandler(file_handler) 

控制日誌格式

缺省狀況下一個處理器只會把信息字符串寫入一個文件或把信息做爲電子郵件發送給你。 可是一個日誌應當記錄更多的信息,因些應該認真地配置日誌記錄器。一個好的日誌不光 記錄爲何會出錯,更重要的是記錄錯在哪裏。

格式化器使用一個格式化字符串做爲實例化時的構造參數,這個字符串中的格式變量會在 日誌記錄時自動轉化。

舉例:

電子郵件
from logging import Formatter mail_handler.setFormatter(Formatter(''' Message type: %(levelname)s Location: %(pathname)s:%(lineno)d Module: %(module)s Function: %(funcName)s Time: %(asctime)s Message: %(message)s ''')) 
日誌文件
from logging import Formatter file_handler.setFormatter(Formatter( '%(asctime)s %(levelname)s: %(message)s ' '[in %(pathname)s:%(lineno)d]' )) 
複雜日誌格式

如下是格式化字符串中一種重要的格式變量。注意,這並不包括所有格式變量,更多變動 參見 logging 包的官方文檔。

格式變量 說明
%(levelname)s 文字形式的日誌等級 ( 'DEBUG' 、 'INFO' 、 'WARNING' 、'ERROR' 和 'CRITICAL' )。
%(pathname)s 調用日誌的源文件的完整路徑(若是可用)。
%(filename)s 調用日誌的源文件文件名。
%(module)s 調用日誌的模塊名。
%(funcName)s 調用日誌的函數名。
%(lineno)d 調用日誌的代碼的行號(若是可用)。
%(asctime)s 調用日誌的時間,缺省格式爲 "2003-07-08 16:49:45,896"(逗號後面的數字爲 毫秒)。經過重載 formatTime() 方法能夠改變 格式。
%(message)s 日誌記錄的消息,同 msg args 。

若是要進一步定製格式,能夠使用格式化器的子類。格式化器有三個有趣的方法:

format():
處理實際的格式化。它接收一個  LogRecord 對象,返回格式化後 的字符串。
formatTime():
它用於  asctime 格式變量。重載它能夠改變時間格式。
formatException()
它用於異常格式化。接收一個  exc_info 元組而且必須返回一個 字符串。缺省狀況下它夠用了,沒必要重載。

更多信息參見官方文檔。

其餘庫

至此,咱們只配置了應用自己的日誌記錄器。其餘庫可能一樣須要記錄日誌。例如, SQLAlchemy 在其核心中大量使用日誌。在 logging 包中有一個方法能夠一次性 地配置全部日誌記錄器,但我不推薦這麼作。由於當你在同一個 Python 解釋器中同時 運行兩個獨立的應用時就沒法使用不一樣的日誌設置了。

相反,我建議使用 getLogger() 函數來鑑別是哪一個日誌記錄器,並獲取 相應的處理器:

from logging import getLogger loggers = [app.logger, getLogger('sqlalchemy'), getLogger('otherlibrary')] for logger in loggers: logger.addHandler(mail_handler) logger.addHandler(file_handler) 

排除應用錯誤

掌握應用錯誤 一文所講的是如何爲生產應用設置日誌和出錯通知。本文要 講的是部署中配置調試的要點和如何使用全功能的 Python 調試器深挖錯誤。

有疑問時,請手動運行

在生產環境中,配置應用時出錯?若是你能夠經過 shell 來訪問主機,那麼請首先在部署 環境中驗證是否能夠經過 shell 手動運行你的應用。請確保驗證時使用的賬戶與配置的 相同,這樣能夠排除用戶權限引起的錯誤。你能夠在你的生產服務器上,使用 Flask 內建 的開發服務器,而且設置 debug=True ,這樣有助於找到配置問題。可是,請 只能在可控的狀況下臨時這樣作 ,毫不能在生產時使用 debug=True 。

使用調試器

爲了更深刻的挖掘錯誤,追蹤代碼的執行, Flask 提供一個開箱即用的調試器(參見 調試模式 )。若是你須要使用其餘 Python 調試器,請注意調試器之間的干擾 問題。在使用你本身的調試器前要作一些參數調整:

  • debug - 是否開啓調試模式並捕捉異常
  • use_debugger - 是否使用 Flask 內建的調試器
  • use_reloader - 出現異常後是否重載或者派生進程

debug 必須設置爲 True (即必須捕獲異常),另兩個隨便。

若是你正在使用 Aptana 或 Eclipse 排錯,那麼 use_debugger 和 use_reloader 都必須設置爲 False 。

一個有用的配置模式以下(固然要根據你的應用調整縮進):

FLASK:
    DEBUG: True
    DEBUG_WITH_APTANA: True

而後,在應用入口( main.py ),修改以下:

if __name__ == "__main__": # 爲了讓 aptana 能夠接收到錯誤,設置 use_debugger=False app = create_app(config="config.yaml") if app.debug: use_debugger = True try: # 若是使用其餘調試器,應當關閉 Flask 的調試器。 use_debugger = not(app.config.get('DEBUG_WITH_APTANA')) except: pass app.run(use_debugger=use_debugger, debug=app.debug, use_reloader=use_debugger, host='0.0.0.0') 

配置管理

New in version 0.3.

應用老是須要必定的配置的。根據應用環境不一樣,會須要不一樣的配置。好比開關調試 模式、設置密鑰以及其餘依賴於環境的東西。

Flask 的設計思路是在應用開始時載入配置。你能夠在代碼中直接硬編碼寫入配置,對於 許多小應用來講這不必定是一件壞事,可是還有更好的方法。

無論你使用何種方式載入配置,均可以使用 Flask 的 config 屬性來操做配置的值。 Flask 自己就使用這個對象來保存 一些配置,擴展也能夠使用這個對象保存配置。同時這也是你保存配置的地方。

配置入門

config 實質上是一個字典的子類,能夠像字典同樣操做:

app = Flask(__name__) app.config['DEBUG'] = True 

某些配置值還轉移到了 Flask 對象中,能夠直接經過 Flask 來操做:

app.debug = True 

一次更新多個配置值能夠使用 dict.update() 方法:

app.config.update( DEBUG=True, SECRET_KEY='...' ) 

內置配置變量

如下配置變量由 Flask 內部使用:

DEBUG 開關調試模式
TESTING 開關測試模式
PROPAGATE_EXCEPTIONS 顯式開關異常的傳播。當 TESTING 或DEBUG 爲真時,老是開啓的。
PRESERVE_CONTEXT_ON_EXCEPTION 缺省狀況下,若是應用在調試模式下運行, 那麼請求環境在發生異常時不會被彈出,以 方便調試器內省數據。能夠經過這個配置來 禁止這樣作。還能夠使用這個配置強制不執行 調試,這樣可能有助於調試生產應用(風險 大)。
SECRET_KEY 密鑰
SESSION_COOKIE_NAME 會話 cookie 的名稱
SESSION_COOKIE_DOMAIN 會話 cookie 的域。若是沒有配置,那麼SERVER_NAME 的全部子域均可以使用 這個 cookie 。
SESSION_COOKIE_PATH 會話 cookie 的路徑。若是沒有配置,那麼 全部 APPLICATION_ROOT 均可以使用 cookie 。若是沒有設置 APPLICATION_ROOT ,那麼 '/' 能夠 使用 cookie 。
SESSION_COOKIE_HTTPONLY 設置 cookie 的 httponly 標誌,缺省爲 True 。
SESSION_COOKIE_SECURE 設置 cookie 的安全標誌,缺省爲 False 。
PERMANENT_SESSION_LIFETIME 常駐會話的存活期,其值是一個datetime.timedelta 對象。 自 Flask 0.8 開始,其值能夠是一個整數, 表示秒數。
USE_X_SENDFILE 開關 x-sendfile
LOGGER_NAME 日誌記錄器的名稱
SERVER_NAME 服務器的名稱和端口號,用於支持子域(如:'myapp.dev:5000' )。注意設置爲 「 localhost 」沒有用,由於 localhost 不 支持子域。設置了 SERVER_NAME 後,在 缺省狀況下會啓用使用應用環境而不使用請求 環境的 URL 生成。
APPLICATION_ROOT 若是應用不佔用整個域或子域,那麼能夠用 這個配置來設定應用的路徑。這個配置還用做 會話 cookie 的路徑。若是使用了整個域, 那麼這個配置的值應當爲 None 。
MAX_CONTENT_LENGTH 這個配置的值單位爲字節,若是設置了,那麼 Flask 會拒絕超過設定長度的請求,返回一個 413 狀態碼。
SEND_FILE_MAX_AGE_DEFAULT send_static_file() ( 缺省靜態文件處理器)和 send_file() 使用的缺省緩存 最大存活期控制,以秒爲單位。把get_send_file_max_age() 分別掛勾到Flask 或 Blueprint 上,能夠重載每一個 文件的值。缺省值爲 43200 ( 12 小時)。
TRAP_HTTP_EXCEPTIONS 若是設置爲 True ,那麼 Flask 將不 執行 HTTP 異常的錯誤處理,而是把它像其它 異常一樣對待並把它壓入異常堆棧。當你在 必須查找出一個 HTTP 異常來自哪裏的狀況下 這個 配置比較有用。
TRAP_BAD_REQUEST_ERRORS Werkzeug 用於處理請求特殊數據的內部數據 結構會引起壞請求異常。一樣,許多操做爲了 一致性會使用一個壞請求隱藏操做失敗。在 這種狀況下,這個配置能夠在調試時辨別到底 爲何會失敗。若是這個配置設爲 True ,那麼就只能獲得一個普通的反饋。
PREFERRED_URL_SCHEME 在沒有可用的模式的狀況下, URL 生成所 使用的 URL 模式。缺省值爲 http 。
JSON_AS_ASCII 缺省狀況下 Flask 把對象序列化爲 ascii-encoded JSON 。若是這個參數值爲 False ,那麼 Flask 就不會把對象編碼 爲 ASCII ,只會原樣輸出返回 unicode 字符 串。 jsonfiy 會自動把對象編碼 utf-8 字符用於傳輸。
JSON_SORT_KEYS 缺省狀況下 Flask 會按鍵值排序 JSON 對象, 這是爲了確保字典的哈希種子的惟一性,返回 值會保持一致,不會破壞外部 HTTP 緩存。 改變這個參數的值就能夠重載缺省的行爲, 重載後可能會提升緩存的性能,可是不推薦 這樣作。
JSONIFY_PRETTYPRINT_REGULAR 若是這個參數設置爲 True (缺省值), 而且若是 jsonify 響應不是被一個 XMLHttpRequest 對象請求的(由 X-Requested-With 頭部控制),那麼 就會被完美打印。

關於 SERVER_NAME 的更多說明

SERVER_NAME 配置用於支持子域。若是要使用子域,那麼就須要這個配置。由於 Flask 在不知道真正服務器名稱的狀況下沒法得知子域。這個配置也用於會話 cookie 。

請記住,不只 Flask 是在使用子域時有這樣的問題,你的瀏覽器一樣如此。大多數 現代瀏覽器不會容許在沒有點的服務器名稱上設置跨子域 cookie 。所以,若是你的 服務器名稱是 'localhost' ,那麼你將不能爲 'localhost' 和全部子域設置 cookie 。在這種狀況下請選擇一個其餘服務器名稱,如 'myapplication.local' 。而且把名稱加上要使用的子域寫入主機配置中或者設置 一個本地 bind 。

New in version 0.4: LOGGER_NAME

New in version 0.5: SERVER_NAME

New in version 0.6: MAX_CONTENT_LENGTH

New in version 0.7: PROPAGATE_EXCEPTIONSPRESERVE_CONTEXT_ON_EXCEPTION

New in version 0.8: TRAP_BAD_REQUEST_ERRORSTRAP_HTTP_EXCEPTIONS,APPLICATION_ROOTSESSION_COOKIE_DOMAINSESSION_COOKIE_PATH,SESSION_COOKIE_HTTPONLYSESSION_COOKIE_SECURE

New in version 0.9: PREFERRED_URL_SCHEME

New in version 0.10: JSON_AS_ASCIIJSON_SORT_KEYS,JSONIFY_PRETTYPRINT_REGULAR

使用配置文件

若是把配置放在一個單獨的文件中會更有用。理想狀況下配置文件應當放在應用包的 外面。這樣能夠在修改配置文件時不影響應用的打包與分發( 使用 Distribute 部署 )。

所以,常見用法以下:

app = Flask(__name__) app.config.from_object('yourapplication.default_settings') app.config.from_envvar('YOURAPPLICATION_SETTINGS') 

首先從 yourapplication.default_settings 模塊載入配置,而後根據YOURAPPLICATION_SETTINGS 環境變量所指向的文件的內容重載配置的值。在 啓動服務器前,在 Linux 或 OS X 操做系統中,這個環境變量能夠在終端中使用 export 命令來設置:

$ export YOURAPPLICATION_SETTINGS=/path/to/settings.cfg
$ python run-app.py
 * Running on http://127.0.0.1:5000/
 * Restarting with reloader...

在 Windows 系統中使用內置的 set 來代替:

>set YOURAPPLICATION_SETTINGS=\path\to\settings.cfg

配置文件自己實質是 Python 文件。只有所有是大寫字母的變量纔會被配置對象所使用。 所以請確保使用大寫字母。

一個配置文件的例子:

# 配置示例
DEBUG = False SECRET_KEY = '?\xbf,\xb4\x8d\xa3"<\x9c\xb0@\x0f5\xab,w\xee\x8d$0\x13\x8b83' 

請確保儘早載入配置,以便於擴展在啓動時能夠訪問相關配置。除了從文件載入配置外, 配置對象還有其餘方法能夠載入配置,詳見 Config 對象的文檔。

配置的最佳實踐

前述的方法的缺點是測試有一點點麻煩。一般解決這個問題沒有標準答案,但有些好的 好的建議:

  1. 在一個函數中建立你的應用並註冊「藍圖」。這樣就能夠使用不一樣配置建立多個 實例,極大方便單元測試。你能夠按需載入配置。
  2. 不要編寫在導入時就訪問配置的代碼。若是你限制本身只能經過請求訪問代碼,那麼 你能夠之後按需配置對象。

開發/生產

大多數應用須要一個以上的配置。最起碼須要一個配置用於生產服務器,另外一個配置用於 開發。應對這種狀況的最簡單的方法老是載入一個缺省配置,並把這個缺省配置做爲版本 控制的一部分。而後,把須要重載的配置,如前文所述,放在一個獨立的文件中:

app = Flask(__name__) app.config.from_object('yourapplication.default_settings') app.config.from_envvar('YOURAPPLICATION_SETTINGS') 

而後你只要增長一個獨立的 config.py 文件並導出YOURAPPLICATION_SETTINGS=/path/to/config.py 就可了。固然還有其餘方法可選, 例如能夠使用導入或子類。

在 Django 應用中,一般的作法是在文件的開關增長 fromyourapplication.default_settings import * 進行顯式地導入,而後手工重載 配置。你還能夠經過檢查一個 YOURAPPLICATION_MODE 之類的環境變量(變量值設置 爲production 或 development 等等)來導入不一樣的配置文件。

一個有趣的方案是使用類和類的繼承來配置:

class Config(object): DEBUG = False TESTING = False DATABASE_URI = 'sqlite://:memory:' class ProductionConfig(Config): DATABASE_URI = 'mysql://user@localhost/foo' class DevelopmentConfig(Config): DEBUG = True class TestingConfig(Config): TESTING = True 

若是要使用這樣的方案,那麼必須使用 from_object():

app.config.from_object('configmodule.ProductionConfig') 

配置的方法多種多樣,由你定度。如下是一些建議:

  • 在版本控制中保存一個缺省配置。要麼在應用中使用這些缺省配置,要麼先導入缺省 配置而後用你本身的配置文件來重載缺省配置。
  • 使用一個環境變量來切換不一樣的配置。這樣就能夠在 Python 解釋器外進行切換,而 根本不用改動代碼,使開發和部署更方便,更快捷。若是你常常在不一樣的項目間 切換,那麼你甚至能夠建立代碼來激活 virtualenv 並導出開發配置。
  • 在生產應用中使用 fabric 之類的工具,向服務器分別傳送代碼和配置。更多細節 參見 使用 Fabric 部署 方案。

實例文件夾

New in version 0.8.

Flask 0.8 引入了實例文件夾。 Flask 花了很長時間纔可以直接使用應用文件夾的路徑( 經過 Flask.root_path )。這也是許多開發者載入應用文件夾外的配置的方法。 不幸的是這種方法只能用於應用不是一個包的狀況下,即根路徑指向包的內容的狀況。

Flask 0.8 引入了一個新的屬性: Flask.instance_path 。它指向一個新名詞: 「實例文件夾」。實例文件夾應當處於版本控制中並進行特殊部署。這個文件夾特別適合 存放須要在應用運行中改變的東西或者配置文件。

能夠要麼在建立 Flask 應用時顯式地提供實例文件夾的路徑,要麼讓 Flask 自動探測 實例文件夾。顯式定義使用 instance_path 參數:

app = Flask(__name__, instance_path='/path/to/instance/folder') 

請記住,這裏提供的路徑 必須 是絕對路徑。

若是 instance_path 參數沒有提供,那麼會使用如下缺省位置:

  • 未安裝的模塊:

    /myapp.py
    /instance
    
  • 未安裝的包:

    /myapp
        /__init__.py
    /instance
    
  • 已安裝的模塊或包:

    $PREFIX/lib/python2.X/site-packages/myapp
    $PREFIX/var/myapp-instance
    

    $PREFIX 是你的 Python 安裝的前綴。多是 /usr 或你的 virtualenv 的 路徑。能夠經過打印 sys.prefix 的值來查看當前的前綴的值。

既然能夠經過使用配置對象來根據關聯文件名從文件中載入配置,那麼就能夠經過改變與 實例路徑相關聯的文件名來按須要載入不一樣配置。在配置文件中的關聯路徑的行爲能夠在 「關聯到應用的根路徑」(缺省的)和 「關聯到實例文件夾」之間變換,具體經過應用 構建函數中的 instance_relative_config 來實現:

app = Flask(__name__, instance_relative_config=True) 

如下是一個完整的配置 Flask 的例子,從一個模塊預先載入配置,而後從配置文件夾中的 一個配置文件(若是這個文件存在的話)載入要重載的配置:

app = Flask(__name__, instance_relative_config=True) app.config.from_object('yourapplication.default_settings') app.config.from_pyfile('application.cfg', silent=True) 

經過 Flask.instance_path 能夠找到實例文件夾的路徑。 Flask 還提供一個打開實例文件夾中的文件的快捷方法: Flask.open_instance_resource() 。

舉例說明:

filename = os.path.join(app.instance_path, 'application.cfg') with open(filename) as f: config = f.read() # 或者經過使用 open_instance_resource: with app.open_instance_resource('application.cfg') as f: config = f.read() 

信號

New in version 0.6.

Flask 自 0.6 版本開始在內部支持信號。信號功能由優秀的 blinker 庫提供支持, 若是沒有安裝該庫就沒法使用信號功能,但不影響其餘功能。

什麼是信號?當核心框架的其餘地方或另外一個 Flask 擴展中發生動做時,信號經過發送 通知來幫助你解耦應用。簡言之,信號容許某個發送者通知接收者有事情發生了。

Flask 自身有許多信號,其餘擴展可能還會帶來更多信號。請記住,信號使用目的是通知 接收者,不該該鼓勵接收者修改數據。你會注意到信號的功能與一些內建的裝飾器相似( 如 request_started 與 before_request() 很是 類似),可是它們的工做原理不一樣。例如核心的 before_request() 處理器以必定的順序執行,而且能夠提早退出請求,返回一個響應。相反,全部的信號 處理器是亂序執行的,而且不修改任何數據。

信號的最大優點是能夠安全快速的訂閱。好比,在單元測試中這些臨時訂閱十分有用。 假設你想知道請求須要渲染哪一個模塊,信號能夠給你答案。

訂閱信號

使用信號的 connect() 方法能夠訂閱該信號。該方法的 第一個參數是當信號發出時所調用的函數。第二個參數是可選參數,定義一個發送者。 使用 disconnect() 方法能夠退訂信號。

全部核心 Flask 信號的發送者是應用自己。所以當訂閱信號時請指定發送者,除非你真的 想要收聽應用的全部信號。當你正在開發一個擴展時,尤爲要注意這點。

下面是一個環境管理器的輔助工具,可用於在單元測試中辨別哪一個模板被渲染了,哪些 變量被傳遞給了模板:

from flask import template_rendered from contextlib import contextmanager @contextmanager def captured_templates(app): recorded = [] def record(sender, template, context, **extra): recorded.append((template, context)) template_rendered.connect(record, app) try: yield recorded finally: template_rendered.disconnect(record, app) 

上例能夠在測試客戶端中輕鬆使用:

with captured_templates(app) as templates: rv = app.test_client().get('/') assert rv.status_code == 200 assert len(templates) == 1 template, context = templates[0] assert template.name == 'index.html' assert len(context['items']) == 10 

爲了使 Flask 在向信號中添加新的參數時不發生錯誤,請確保使用一個額外的 **extra參數。

在 with 代碼塊中,全部由 app 渲染的模板會被記錄在 templates 變量中。每當有 模板被渲染,模板對象及環境就會追加到變量中。

另外還有一個方便的輔助方法( connected_to() )。它 容許臨時把一個使用環境對象的函數訂閱到一個信號。由於環境對象的返回值不能被 指定,因此必須把列表做爲參數:

from flask import template_rendered def captured_templates(app, recorded, **extra): def record(sender, template, context): recorded.append((template, context)) return template_rendered.connected_to(record, app) 

上例能夠這樣使用:

templates = [] with captured_templates(app, templates, **extra): ... template, context = templates[0] 

Blinker API 變化

Blinker version 1.1 版本中增長了 connected_to() 方法。

建立信號

若是相要在你本身的應用中使用信號,那麼能夠直接使用 blinker 庫。最多見的,也是最 推薦的方法是在自定義的 Namespace 中命名信號:

from blinker import Namespace my_signals = Namespace() 

接着能夠像這樣建立新的信號:

model_saved = my_signals.signal('model-saved') 

信號的名稱應當是惟一的,而且應當簡明以便於調試。能夠經過 name 屬性得到信號的名稱。

擴展開發者注意

若是你正在編寫一個 Flask 擴展,而且想要妥善處理 blinker 安裝缺失的狀況,那麼 能夠使用 flask.signals.Namespace 類。

發送信號

若是想要發送信號,能夠使用 send() 方法。它的第一個 參數是一個發送者,其餘參數要發送給訂閱者的東西,其餘參數是可選的:

class Model(object): ... def save(self): model_saved.send(self) 

請謹慎選擇發送者。若是是一個發送信號的類,請把 self 做爲發送者。若是發送信號 的是一個隨機的函數,那麼能夠把 current_app._get_current_object() 做爲 發送者。

傳遞代理做爲發送者

不要把 current_app 做爲發送者傳遞給信號。請使用current_app._get_current_object() 。由於 current_app 是 一個代理,不是實際的應用對象。

信號與 Flask 的請求環境

信號在接收時,徹底支持 請求環境 。在 request_started 和 request_finished 本地環境變量 始終可用。所以你能夠依賴 flask.g 及其餘本地環境變量。 請注意在 發送信號 中所述的限制和 request_tearing_down 信號。

信號訂閱裝飾器

Blinker 1.1 版本中你還能夠經過使用新的 connect_via() 裝飾器輕鬆訂閱信號:

from flask import template_rendered @template_rendered.connect_via(app) def when_template_rendered(sender, template, context, **extra): print 'Template %s is rendered with %s' % (template.name, context) 

核心信號

Flask 中有如下信號:

flask. template_rendered

這個信號發送於一個模板被渲染成功後。信號傳遞的 template 是模板的實例,context 是環境對象是一個字典。

訂閱示例:

def log_template_renders(sender, template, context, **extra): sender.logger.debug('Rendering template "%s" with context %s', template.name or 'string template', context) from flask import template_rendered template_rendered.connect(log_template_renders, app) 
flask. request_started

這個信號發送於請求開始以前,且請求環境設置完成以後。由於請求環境已經綁定, 因此訂閱者能夠用標準的全局代理,如 request 來操做請求。

訂閱示例:

def log_request(sender, **extra): sender.logger.debug('Request context is set up') from flask import request_started request_started.connect(log_request, app) 
flask. request_finished

這個信號發送於向客戶端發送響應以前。信號傳遞的 response 爲將要發送的響應。

訂閱示例:

def log_response(sender, response, **extra): sender.logger.debug('Request context is about to close down. ' 'Response: %s', response) from flask import request_finished request_finished.connect(log_response, app) 
flask. got_request_exception

這個信號發送於請求進行中發生異常的時候。它的發送 早於 標準異常處理介於。 在調試模式下,雖然沒有異常處理,但發生異常時也發送這個信號。信號傳遞的exception 是異常對象。

訂閱示例:

def log_exception(sender, exception, **extra): sender.logger.debug('Got exception during processing: %s', exception) from flask import got_request_exception got_request_exception.connect(log_exception, app) 
flask. request_tearing_down

這個信號發送於請求崩潰的時候,無論是否引起異常。目前,偵聽此信號的函數在通常 崩潰處理器後調用,可是沒有什麼東西可用。

訂閱示例:

def close_db_connection(sender, **extra): session.close() from flask import appcontext_tearing_down request_tearing_down.connect(close_db_connection, app) 

在 Flask 版本 0.9 中,這還會傳遞一個 exc 關鍵字參數,若是這個參數存在的話。 這個參數是引起崩潰的異常的引用。

flask. appcontext_tearing_down

當應用環境崩潰時發送這個信號。這個信號老是會發送,甚至是由於一個異常引起的 崩潰。偵聽這個信號的函數會在常規崩潰處理器後被調用,可是你沒法回饋這個信號。

訂閱示例:

def close_db_connection(sender, **extra): session.close() from flask import request_tearing_down appcontext_tearing_down.connect(close_db_connection, app) 

這還會傳遞一個 exc 關鍵字參數,若是這個參數存在的話。這個參數是引起崩潰的 異常的引用。

flask. appcontext_pushed

當一個應用的環境被壓入時,應用會發送這個信號。這個信號一般用於在單元測試中 臨時鉤接信息。例如能夠用於改變 g 對象中現存的資源。

用法示例:

from contextlib import contextmanager from flask import appcontext_pushed @contextmanager def user_set(app, user): def handler(sender, **kwargs): g.user = user with appcontext_pushed.connected_to(handler, app): yield 

在測試代碼中這樣寫:

def test_user_me(self): with user_set(app, 'john'): c = app.test_client() resp = c.get('/users/me') assert resp.data == 'username=john' 

New in version 0.10.

appcontext_popped

當一個應用的環境被彈出時,應用會發送這個信號。這個信號一般寫成appcontext_tearing_down 信號。

New in version 0.10.

flask. message_flashed

當應用閃現一個消息時會發出這個信號。message`參數是消息內容, `category 參數是消息類別。

訂閱示例:

recorded = [] def record(sender, message, category, **extra): recorded.append((message, category)) from flask import message_flashed message_flashed.connect(record, app) 

New in version 0.10.

可插撥視圖

New in version 0.7.

Flask 0.7 版本引入了可插撥視圖。可插撥視圖基於使用類來代替函數,其靈感來自於 Django 的通用視圖。可插撥視圖的主要用途是用可定製的、可插撥的視圖來替代部分 實現。

基本原理

假設有一個函數用於從數據庫中載入一個對象列表並在模板中渲染:

@app.route('/users/') def show_users(page): users = User.query.all() return render_template('users.html', users=users) 

上例簡單而靈活。可是若是要把這個視圖變成一個能夠用於其餘模型和模板的通用視圖, 那麼這個視圖仍是不夠靈活。所以,咱們就須要引入可插撥的、基於類的視圖。第一步, 能夠把它轉換爲一個基礎視圖:

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

就如你所看到的,必須作的是建立一個 flask.views.View 的子類,而且執行dispatch_request() 。而後必須經過使用 as_view() 方法把類轉換爲實際視圖函數。傳遞給函數的 字符串是最終視圖的名稱。可是這自己沒有什麼幫助,因此讓咱們來小小地重構一下:

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 指向什麼?解決之道是:每當請求發出時 就建立一個類的新實例,而且根據來自 URL 規則的參數調用 dispatch_request() 方法。類自己根據參數實例化後傳遞給 as_view() 函數。例如能夠這樣寫一個類:

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

方法提示

可插撥視圖能夠像普通函數同樣加入應用。加入的方式有兩種,一種是使用 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 方法提供相對應的不一樣函數顯得尤其有用。使用 flask.views.MethodView 能夠輕易作到這點。在這個類中,每一個 HTTP 方法 都映射到一個同名函數(函數名稱爲小寫字母):

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 屬性,它會自動使用相應 的類方法。

裝飾視圖

視圖函數會被添加到路由系統中,而視圖類則不會。所以視圖類不須要裝飾,只能以手工 使用 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] 

請牢記:由於從調用者的角度來看,類的 self 被隱藏了,因此不能在類的方法上單獨 使用裝飾器。

用於 API 的方法視圖

網絡 API 常常直接對應 HTTP 變量,所以頗有必要實現基於 MethodView 的 API 。即多數時候, API 須要把不一樣的 URL 規則應用到同一個方法視圖。例如,假設你須要這樣使用一個 user 對象:

URL 方法 說明
/users/ GET 給出一個包含全部用戶的列表
/users/ POST 建立一個新用戶
/users/<id> GET 顯示一個用戶
/users/<id> PUT 更新一個用戶
/users/<id> DELETE 刪除一個用戶

那麼如何使用 MethodView 來實現呢?方法是使用多個規則對應 到同一個視圖。

假設視圖是這樣的:

class UserAPI(MethodView): def get(self, user_id): if user_id is None: # 返回一個包含全部用戶的列表 pass else: # 顯示一個用戶 pass def post(self): # 建立一個新用戶 pass def delete(self, user_id): # 刪除一個用戶 pass def put(self, user_id): # update a single user pass 

那麼如何把這個視圖掛接到路由系統呢?方法是增長兩個規則併爲每一個規則顯式聲明 方法:

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

應用環境

New in version 0.9.

Flask 的設計思路之一是代碼有兩種不一樣的運行「狀態」。一種是「安裝」狀態,從 Flask 對象被實例化後開始,到第一個請求到來以前。在這種狀態下,如下規則 成立:

  • 能夠安全地修改應用對象。
  • 尚未請求須要處理。
  • 沒有應用對象的引用,所以沒法修改應用對象。

相反,在請求處理時,如下規則成立:

  • 當請求活動時,本地環境對象 ( flask.request 或其餘對象)指向當前 請求。
  • 這些本地環境對象能夠任意修改。

除了上述兩種狀態以外,還有介於二者之間的第三種狀態。這種狀態下,你正在處理應用, 可是沒有活動的請求。就相似於,你坐在 Python 交互終端前,與應用或一個命令行程序 互動。

應用環境是 current_app 本地環境的源動力。

應用環境的做用

應用環境存在的主要緣由是,之前過多的功能依賴於請求環境,缺少更好的處理方案。 Flask 的一個重要設計原則是能夠在同一個 Pyhton 進程中運行多個應用。

那麼,代碼如何找到「正確」的應用呢?之前咱們推薦顯式地傳遞應用,可是有可能會 引起庫與庫之間的干擾。

問題的解決方法是使用 current_app 代理。 current_app 是綁定到當前請求的應用的引用。可是沒有請求的狀況下 使用請求環境是一件奢侈的事情,因而就引入了應用環境。

建立一個應用環境

建立應用環境有兩種方法。一種是隱式的:當一個請求環境被建立時,若是有須要就會 相應建立一個應用環境。所以,你能夠忽略應用環境的存在,除非你要使用它。

另外一種是顯式的,使用 app_context() 方法:

from flask import Flask, current_app app = Flask(__name__) with app.app_context(): # 在本代碼塊中, current_app 指向應用。 print current_app.name 

在 SERVER_NAME 被設置的狀況下,應用環境還被 url_for() 函數 使用。這樣可讓你在沒有請求的狀況下生成 URL 。

環境的做用域

應用環境按需建立和消滅,它歷來不在線程之間移動,也不被不一樣請求共享。所以,它是 一個存放數據庫鏈接信息和其餘信息的好地方。內部的棧對象被稱爲flask._app_ctx_stack 。擴展能夠在棧的最頂端自由儲存信息,前提是使用惟一 的名稱,而 flask.g 對象是留給用戶使用的。

更多信息參見 Flask 擴展開發 。

環境的用法

環境的典型用途是緩存一個請求須要預備或使用的資源,例如一個數據庫鏈接。由於環境 是一個 Flask 的應用和擴展共享的地方,因此儲存的資源必須使用獨一無二的名稱。

最多見的用法是把資源管理分爲如下兩個部分:

  1. 在環境中緩存一個隱式的資源。
  2. 資源釋放後的環境解散。

一般會有一個形如 get_X() 函數,這個函數的用途是當資源 X 存在時就返回 這個資源,不然就建立這個資源。還會有一個 teardown_X() 函數用做解散句柄。

這是一個鏈接數據庫的例子:

import sqlite3 from flask import g def get_db(): db = getattr(g, '_database', None) if db is None: db = g._database = connect_to_database() return db @app.teardown_appcontext def teardown_db(exception): db = getattr(g, '_database', None) if db is not None: db.close() 

第一次調用 get_db() 時,鏈接將會被創建。創建的過程當中隱式地使用了一個LocalProxy 類:

from werkzeug.local import LocalProxy db = LocalProxy(get_db) 

這樣,用戶就能夠經過 get_db() 來直接訪問 db 了。

請求環境

本文講述 Flask 0.7 版本的運行方式,與舊版本的運行方式基本相同,但也有一些細微的 差異。

建議你在閱讀本文以前,先閱讀 應用環境 。

深刻本地環境

假設有一個工具函數,這個函數返回用戶重定向的 URL (包括 URL 的 next 參數、 或 HTTP 推薦 和索引頁面):

from flask import request, url_for def redirect_url(): return request.args.get('next') or \ request.referrer or \ url_for('index') 

如上例所示,這個函數訪問了請求對象。若是你在一個普通的 Python 解釋器中運行這個 函數,那麼會看到以下異常:

>>> redirect_url() Traceback (most recent call last): File "<stdin>", line 1, in <module> AttributeError: 'NoneType' object has no attribute 'request' 

這是由於如今咱們沒有一個能夠訪問的請求。因此咱們只能建立一個請求並綁定到當前 環境中。 test_request_context 方法能夠建立一個 RequestContext :

>>> ctx = app.test_request_context('/?next=http://example.com/') 

這個環境有兩種使用方法:一種是使用 with 語句;另外一種是調用 push() 和 pop() 方法:

>>> ctx.push() 

如今能夠使用請求對象了:

>>> redirect_url() u'http://example.com/' 

直到你調用 pop :

>>> ctx.pop() 

能夠把請求環境理解爲一個堆棧,能夠屢次壓入和彈出,能夠方便地執行一個像內部 重定向之類的東西。

關於在 Python 解釋器中使用請求環境的更多內容參見 在 Shell 中使用 Flask 。

環境的工做原理

若是深刻 Flask WSGI 應用內部,那麼會找到相似以下代碼:

def wsgi_app(self, environ): with self.request_context(environ): try: response = self.full_dispatch_request() except Exception, e: response = self.make_response(self.handle_exception(e)) return response(environ, start_response) 

request_context() 方法返回一個新的 RequestContext 對象,而且使用 with 語句把這個對象綁定 到環境。在 with 語句塊中,在同一個線程中調用的全部東西能夠訪問全局請求 (flask.request 或其餘)。

請求環境的工做方式就像一個堆棧,棧頂是當前活動請求。 push() 把環境壓入堆棧中,而 pop() 把環境彈出。彈出的同時,會執行應用的 teardown_request() 函數。

另外一件要注意的事情是:請求環境會在壓入時自動建立一個 應用環境 。在此以前,應用沒有應用環境。

回調和錯誤處理

若是在請求處理的過程當中發生錯誤,那麼 Flask 會如何處理呢?自 Flask 0.7 版本以後, 處理方式有所改變。這是爲了更方便地反映到底發生了什麼狀況。新的處理方式很是簡單:

  1. 在每一個請求以前,會執行全部 before_request() 函數。若是 其中一個函數返回一個響應,那麼其餘函數將再也不調用。可是在任何狀況下,這個 返回值將會替代視圖的返回值。
  2. 若是 before_request() 函數均沒有響應,那麼就會進行正常的 請求處理,匹配相應的視圖,返回響應。
  3. 接着,視圖的返回值會轉換爲一個實際的響應對象並交給 after_request() 函數處理。在處理過程當中,這個對象可能會被 替換或修改。
  4. 請求處理的最後一環是執行 teardown_request() 函數。這類 函數在任何狀況下都會被執行,甚至是在發生未處理異常或請求預處理器沒有執行( 例如在測試環境下,有時不想執行)的狀況下。

那麼若是出錯了會怎麼樣?在生產模式下,若是一個異常未被主要捕獲處理,那麼會調用 500 內部服務器處理器。在開發模式下,引起的異常再也不被進一步處理,會提交給 WSGI 服務器。所以,須要使用交互調試器來查看調試信息。

Flask 0.7 版本的重大變化是內部服務器錯誤再也不由請求後回調函數來處理,而且請求後 回調函數也不保證必定被執行。這樣使得內部調試代碼更整潔、更易懂和更容易定製。

同時還引入了新的卸載函數,這個函數在請求結束時必定會執行。

卸載回調函數

卸載回調函數的特殊之處在於其調用的時機是不固定的。嚴格地說,調用時機取決於 其綁定的 RequestContext 對象的生命週期。當請求環境彈出時就 會調用teardown_request() 函數。

請求環境的生命週期是會變化的,當請求環境位於測試客戶端中的 with 語句中或者在 命令行下使用請求環境時,其生命週期會被延長。所以知道生命週期是否被延長是很重要 的:

with app.test_client() as client: resp = client.get('/foo') # 到這裏尚未調用卸載函數。即便這時響應已經結束,而且已經 # 得到響應對象,仍是不會調用卸載函數。 # 只有到這裏纔會調用卸載函數。另外,若是另外一個請求在客戶端中被 # 激發,也會調用卸載函數。 

在使用命令行時,能夠清楚地看到運行方式:

>>> app = Flask(__name__) >>> @app.teardown_request ... def teardown_request(exception=None): ... print 'this runs after request' ... >>> ctx = app.test_request_context() >>> ctx.push() >>> ctx.pop() this runs after request >>> 

記牢記:卸載函數在任何狀況下都會被執行,甚至是在請求預處理回調函數沒有執行, 可是發生異常的狀況下。有的測試系統可能會臨時建立一個請求環境,可是不執行 預處理器。請正確使用卸載處理器,確保它們不會執行失敗。

關於代理

部分 Flask 提供的對象是其餘對象的代理。使用代理的緣由是代理對象共享於不一樣的 線程,它們在後臺根據須要把實際的對象分配給不一樣的線程。

多數狀況下,你不須要關心這個。可是也有例外,在下列狀況有下,知道對象是一個代理 對象是有好處的:

  • 想要執行真正的實例檢查的狀況。由於代理對象不會假冒被代理對象的對象類型, 所以,必須檢查被代理的實際對象(參見下面的 _get_current_object )。
  • 對象引用很是重要的狀況(例如發送 信號 )。

若是想要訪問被代理的對象,能夠使用 _get_current_object() 方法:

app = current_app._get_current_object() my_signal.send(app) 

出錯時的環境保存

無論是否出錯,在請求結束時,請求環境會被彈出,而且全部相關聯的數據會被銷燬。 可是在開發過程當中,可能須要在出現異常時保留相關信息。在 Flask 0.6 版本及更早的 版本中,在發生異常時,請求環境不會被彈出,以便於交互調試器提供重要信息。

自 Flask 0.7 版本開始,能夠經過設置 PRESERVE_CONTEXT_ON_EXCEPTION 配置變量 來更好地控制環境的保存。缺省狀況下,這個配置變動與 DEBUG 變動關聯。若是在 調試模式下,那麼環境會被保留,而在生產模式下則不保留。

不要在生產環境下強制激活 PRESERVE_CONTEXT_ON_EXCEPTION ,由於這會在出現異常 時致使應用內存溢出。可是在調試模式下使用這個變動是十分有用的,你能夠得到在生產 模式下出錯時的環境。

使用藍圖的模塊化應用

New in version 0.7.

爲了在一個或多個應用中,使應用模塊化而且支持經常使用方案, Flask 引入了 藍圖 概念。藍圖能夠極大地簡化大型應用併爲擴展提供集中的註冊入口。 Blueprint 對象與Flask 應用對象的工做方式相似,但不是一個真正 的應用。它更像一個用於構建和擴展應用的 藍圖 。

爲何使用藍圖?

Flask 中藍圖有如下用途:

  • 把一個應用分解爲一套藍圖。這是針對大型應用的理想方案:一個項目能夠實例化一個 應用,初始化多個擴展,並註冊許多藍圖。
  • 在一個應用的 URL 前綴和(或)子域上註冊一個藍圖。 URL 前綴和(或)子域的參數 成爲藍圖中全部視圖的通用視圖參數(缺省狀況下)。
  • 使用不一樣的 URL 規則在應用中屢次註冊藍圖。
  • 經過藍圖提供模板過濾器、靜態文件、模板和其餘工具。藍圖沒必要執行應用或視圖 函數。
  • 當初始化一個 Flask 擴展時,爲以上任意一種用途註冊一個藍圖。

Flask 中的藍圖不是一個可插撥的應用,由於它不是一個真正的應用,而是一套能夠註冊 在應用中的操做,而且能夠註冊屢次。那麼爲何不使用多個應用對象呢?能夠使用多個 應用對象(參見 應用調度 ),可是這樣會致使每一個應用都使用本身獨立的 配置,且只能在 WSGI 層中管理應用。

而若是使用藍圖,那麼應用會在 Flask 層中進行管理,共享配置,經過註冊按需改變應用 對象。藍圖的缺點是一旦應用被建立後,只有銷燬整個應用對象才能註銷藍圖。

藍圖的概念

藍圖的基本概念是:在藍圖被註冊到應用以後,所要執行的操做的集合。當分配請求時, Flask 會把藍圖和視圖函數關聯起來,並生成兩個端點以前的 URL 。

第一個藍圖

如下是一個最基本的藍圖示例。在這裏,咱們將使用藍圖來簡單地渲染靜態模板:

from flask import Blueprint, render_template, abort from jinja2 import TemplateNotFound simple_page = Blueprint('simple_page', __name__, template_folder='templates') @simple_page.route('/', defaults={'page': 'index'}) @simple_page.route('/<page>') def show(page): try: return render_template('pages/%s.html' % page) except TemplateNotFound: abort(404) 

當你使用 @simple_page.route 裝飾器綁定一個函數時,藍圖會記錄下所登記的 show 函數。當之後在應用中註冊藍圖時,這個函數會被註冊到應用中。另外,它會把 構建Blueprint 時所使用的名稱(在本例爲 simple_page )做爲函數端點 的前綴。

註冊藍圖

能夠這樣註冊藍圖:

from flask import Flask from yourapplication.simple_page import simple_page app = Flask(__name__) app.register_blueprint(simple_page) 

如下是註冊藍圖後造成的規則:

[<Rule '/static/<filename>' (HEAD, OPTIONS, GET) -> static>,
 <Rule '/<page>' (HEAD, OPTIONS, GET) -> simple_page.show>,
 <Rule '/' (HEAD, OPTIONS, GET) -> simple_page.show>]

第一條很明顯,是來自於應用自己的用於靜態文件的。後面兩條是用於藍圖simple_page 的 show 函數的。你能夠看到,它們的前綴都是藍圖的名稱,而且 使用一個點( . )來分隔。

藍圖還能夠掛接到不一樣的位置:

app.register_blueprint(simple_page, url_prefix='/pages') 

這樣就會造成以下規則:

[<Rule '/static/<filename>' (HEAD, OPTIONS, GET) -> static>,
 <Rule '/pages/<page>' (HEAD, OPTIONS, GET) -> simple_page.show>,
 <Rule '/pages/' (HEAD, OPTIONS, GET) -> simple_page.show>]

總之,你能夠屢次註冊藍圖,可是不必定每一個藍圖都能正確響應。是否可以屢次註冊實際 上取決於你的藍圖是如何編寫的,是否能根據不一樣的位置作出正確的響應。

藍圖資源

藍圖還能夠用於提供資源。有時候,咱們僅僅是爲了使用一些資源而使用藍圖。

藍圖資源文件夾

和普通應用同樣,藍圖通常都放在一個文件夾中。雖然多個藍圖能夠共存於同一個文件夾 中,可是最好不要這樣作。

文件夾由 Blueprint 的第二個參數指定,一般爲 __name__ 。這個參數指定 與藍圖相關的邏輯 Python 模塊或包。若是這個參數指向的是實際的 Python 包(文件 系統中的一個文件夾),那麼它就是資源文件夾。若是是一個模塊,那麼這個模塊包含的 包就是資源文件夾。能夠經過 Blueprint.root_path 屬性來查看藍圖的資源 文件夾:

>>> simple_page.root_path '/Users/username/TestProject/yourapplication' 

能夠使用 open_resource() 函數快速打開這個文件夾中的資源:

with simple_page.open_resource('static/style.css') as f: code = f.read() 
靜態文件

藍圖的第三個參數是 static_folder 。這個參數用以指定藍圖的靜態文件所在的 文件夾,它能夠是一個絕對路徑也能夠是相對路徑。:

admin = Blueprint('admin', __name__, static_folder='static') 

缺省狀況下,路徑最右端的部分是在 URL 中暴露的部分。上例中的文件夾爲 static ,那麼 URL 應該是藍圖加上 /static 。藍圖註冊爲 /admin , 那麼靜態文件夾就是/admin/static 。

端點的名稱是 blueprint_name.static ,所以你能夠使用和應用中的文件夾同樣的方法 來生成其 URL:

url_for('admin.static', filename='style.css') 
模板

若是你想使用藍圖來暴露模板,那麼能夠使用 Blueprint 的 template_folder 參數:

admin = Blueprint('admin', __name__, template_folder='templates') 

和靜態文件同樣,指向藍圖資源文件夾的路徑能夠是絕對的也能夠是相對的。藍圖中的 模板文件夾會被添加到模板搜索路徑中,但其優先級低於實際應用的模板文件夾。這樣在 實際應用中能夠方便地重載藍圖提供的模板。

假設你的藍圖便於 yourapplication/admin 中,要渲染的模板是 'admin/index.html', template_folder 參數值爲 templates ,那麼真正的 模板文件爲:yourapplication/admin/templates/admin/index.html 。

更詳細一點說:若是你有一個名爲 admin 的藍圖,該藍圖指定的模版文件是 index.html,那麼最好按照以下結構存放模版文件:

yourpackage/
    blueprints/
        admin/
            templates/
                admin/
                    index.html
            __init__.py

這樣,當你須要渲染模板的時候就能夠使用 admin/index.html 來找到模板。 若是沒有載入正確的模板,那麼應該啓用 EXPLAIN_TEMPLATE_LOADING 配置變量。 啓用這個變量之後,每次調用 render_template 時, Flask 會打印出定位模板的 步驟,方便調試。

建立 URL

若是要建立頁面連接,能夠和一般同樣使用 url_for() 函數,只是要把藍圖名稱做爲端點的前綴,而且用一個點( . )來 分隔:

url_for('admin.index') 

另外,若是在一個藍圖的視圖函數或者被渲染的模板中須要連接同一個藍圖中的其餘 端點,那麼使用相對重定向,只使用一個點使用爲前綴:

url_for('.index') 

若是當前請求被分配到 admin 藍圖端點時,上例會連接到 admin.index 。

錯誤處理器

藍圖和 Flask 應用對象同樣支持錯誤處理器裝飾器,所以爲藍圖自定義錯誤 處理頁面很是方便。

下面是一個處理「 404 頁面沒法找到 」的例子:

@simple_page.errorhandler(404) def page_not_found(e): return render_template('pages/404.html') 

更多信息參見 errorpages 。

Flask 擴展

Flask 擴展以各類方式擴展了 Flask 的功能,好比加強對數據庫的支持等等。

查找擴展

Flask 擴展都列在 Flask 擴展註冊 中,而且能夠使用 easy_install 或 pip 下載。若是你把一個擴展做爲依賴添加到你的 requirements.rst 或 setup.py 文件,那麼它們能夠使用一個簡單的命令安裝或隨着應用一塊兒安裝。

使用擴展

擴展通常都有說明如何使用的文檔,這些文檔應該和擴展一塊兒發行。擴展如何運行沒有 統一的要求,可是通常在常見位置導入擴展。假設一個擴展稱爲 Flask-Foo 或 Foo-Flask ,那麼老是能夠導入 flask.ext.foo:

from flask.ext import foo 

Flask 0.8 之前的版本

若是你正在使用 Flask 0.7 版本或更早版本, flask.ext 包是不存在的。你 必須根據擴展的發行方式導入 flaskext.foo 或 flask_foo 。若是你要開發一個 支持 Flask 0.7 版本或更早版本的應用,那麼你應當仍是從 flask.ext 包中 導入。咱們提供了一個兼容模塊用以兼容老版本的 Flask ,你能夠從 github 下載: flaskext_compat.py

使用方法以下:

import flaskext_compat flaskext_compat.activate() from flask.ext import foo 

一旦 flaskext_compat 模塊被激活, flask.ext 就會存在,就能夠從這個 包導入擴展。

在 Shell 中使用 Flask

New in version 0.3.

喜歡 Python 的緣由之一是交互式的 shell ,它可讓你實時運行 Python 命令,而且 當即獲得結果。 Flask 自己不帶交互 shell ,由於它不須要特定的前期設置,只要在 shell 中導入你的應用就能夠開始使用了。

有些輔助工具可讓你在 shell 中更舒服。在交互終端中最大的問題是你不會像瀏覽器 同樣觸發一個請求,這就意味着沒法使用 g 和 request 等對象。那麼如何在 shell 中測試依賴這些對象的代碼呢?

這裏有一些有用的輔助函數。請記住,這些輔助函數不只僅只能用於 shell ,還能夠用於 單元測試和其餘須要假冒請求環境的狀況下。

在讀下去以前最好你已經讀過 請求環境 一節。

建立一個請求環境

在 shell 中建立一個正確的請求環境的最簡便的方法是使用 test_request_context 方法。這個方法會建立一個 RequestContext :

>>> ctx = app.test_request_context() 

一般你會使用 with 語句來激活請求對象,可是在 shell 中,能夠簡便地手動使用 push()和 pop() 方法:

>>> ctx.push() 

從這裏開始,直到調用 pop 以前,你能夠使用請求對象:

>>> ctx.pop() 

發送請求前/後動做

僅僅建立一個請求環境仍是不夠的,須要在請求前運行的代碼仍是沒有運行。好比,在 請求前能夠會須要轉接數據庫,或者把用戶信息儲存在 g 對象中。

使用 preprocess_request() 能夠方便地模擬請求前/後動做:

>>> ctx = app.test_request_context() >>> ctx.push() >>> app.preprocess_request() 

請記住, preprocess_request() 函數能夠會返回一個響應對象。 若是返回的話請忽略它。

若是要關閉一個請求,那麼你須要在請求後函數(由 process_response() 觸發)做用於響應對象前關閉:

>>> app.process_response(app.response_class()) <Response 0 bytes [200 OK]> >>> ctx.pop() 

teardown_request() 函數會在環境彈出後自動執行。咱們能夠使用 這些函數來銷燬請求環境所須要使用的資源(如數據庫鏈接)。

在 Shell 中玩得更爽

若是你喜歡在 shell 中的感受,那麼你能夠建立一個導入有關東西的模塊,在模塊中還 能夠定義一些輔助方法,如初始化數據庫或者刪除表等等。假設這個模塊名爲 shelltools ,那麼在開始時你能夠:

>>> from shelltools import * 

Flask 方案

有一些東西是大多數網絡應用都會用到的。好比許多應用都會使用關係型數據庫和用戶 驗證,在請求以前鏈接數據庫並獲得當前登陸用戶的信息,在請求以後關閉數據庫鏈接。

更多用戶貢獻的代碼片段和方案參見 Flask 代碼片段歸檔 。

大型應用

對於大型應用來講使用包代替模塊是一個好主意。使用包很是簡單。假設有一個小應用如 下:

/yourapplication
    /yourapplication.py
    /static
        /style.css
    /templates
        layout.html
        index.html
        login.html
        ...
簡單的包

要把上例中的小應用裝換爲大型應用只要在現有應用中建立一個名爲 yourapplication 的新文件夾,並把全部東西都移動到這個文件夾內。而後把 yourapplication.py 改名 爲__init__.py 。(請首先刪除全部 .pyc 文件,不然基本上會出問題)

修改完後應該以下例:

/yourapplication
    /yourapplication
        /__init__.py
        /static
            /style.css
        /templates
            layout.html
            index.html
            login.html
            ...

可是如今如何運行應用呢?本來的 python yourapplication/__init__.py 沒法運行 了。由於 Python 不但願包內的模塊成爲啓動文件。可是這不是一個大問題,只要在yourapplication 文件夾旁添加一個 runserver.py 文件就能夠了,其內容以下:

from yourapplication import app app.run(debug=True) 

咱們從中學到了什麼?如今咱們來重構一下應用以適應多模塊。只要記住如下幾點:

  1. Flask 應用對象必須位於 __init__.py 文件中。這樣每一個模塊就能夠安全地導入 了,且 __name__ 變量會解析到正確的包。
  2. 全部視圖函數(在頂端有 route() 的)必須在 __init__.py 文件中被導入。不是導入對象自己,而是導入視圖模塊。請 在應用對象建立以後 導入視圖對象。

__init__.py 示例:

from flask import Flask app = Flask(__name__) import yourapplication.views 

views.py 內容以下:

from yourapplication import app @app.route('/') def index(): return 'Hello World!' 

最終所有內容以下:

/yourapplication
    /runserver.py
    /yourapplication
        /__init__.py
        /views.py
        /static
            /style.css
        /templates
            layout.html
            index.html
            login.html
            ...

迴環導入

迴環導入是指兩個模塊互相導入,本例中咱們添加的 views.py 就與 __init__.py 相互依賴。每一個 Python 程序員都討厭迴環導入。通常狀況下回環導入是個壞主意,但 在這裏一點問題都沒有。緣由是咱們沒有真正使用 __init__.py 中的視圖,只是 保證模塊被導入,而且咱們在文件底部才這樣作。

可是這種方式仍是有些問題,由於沒有辦法使用裝飾器。要找到解決問題的靈感請參閱大型應用 一節。

使用藍圖

對於大型應用推薦把應用分隔爲小塊,每一個小塊使用藍圖輔助執行。關於這個主題的介紹 請參閱 使用藍圖的模塊化應用 一節 。

應用工廠

若是你已經在應用中使用了包和藍圖( 使用藍圖的模塊化應用 ),那麼還有許多方法能夠更 進一步地改進你的應用。經常使用的方案是導入藍圖後建立應用對象,可是若是在一個函數中 建立對象,那麼就能夠建立多個實例。

那麼這樣作有什麼用呢?

  1. 用於測試。能夠針對不一樣的狀況使用不一樣的配置來測試應用。
  2. 用於多實例,若是你須要運行同一個應用的不一樣版本的話。固然你能夠在服務器上 使用不一樣配置運行多個相同應用,可是若是使用應用工廠,那麼你能夠只使用一個 應用進程而獲得多個應用實例,這樣更容易操控。

那麼如何作呢?

基礎工廠

方法是在一個函數中設置應用,具體以下:

def create_app(config_filename): app = Flask(__name__) app.config.from_pyfile(config_filename) from yourapplication.model import db db.init_app(app) from yourapplication.views.admin import admin from yourapplication.views.frontend import frontend app.register_blueprint(admin) app.register_blueprint(frontend) return app 

這個方法的缺點是在導入時沒法在藍圖中使用應用對象。可是你能夠在一個請求中使用它。 如何經過配置來訪問應用?使用 current_app:

from flask import current_app, Blueprint, render_template admin = Blueprint('admin', __name__, url_prefix='/admin') @admin.route('/') def index(): return render_template(current_app.config['INDEX_TEMPLATE']) 

這裏咱們在配置中查找模板的名稱。

擴展對象初始化時不會綁定到一個應用,應用能夠使用 db.init_app 來設置擴展。 擴展對象中不會儲存特定應用的狀態,所以一個擴展能夠被多個應用使用。關於擴展設計 的更多信息請參閱 Flask 擴展開發 。

當使用 Flask-SQLAlchemy 時,你的 model.py 多是這樣的:

from flask.ext.sqlalchemy import SQLAlchemy # no app object passed! Instead we use use db.init_app in the factory. db = SQLAlchemy() # create some models 
使用應用

所以,要使用這樣的應用就必須先建立它。下面是一個運行應用的示例 run.py 文件:

from yourapplication import create_app app = create_app('/path/to/config.cfg') app.run() 
改進工廠

上面的工廠函數還不是足夠好,能夠改進的地方主要有如下幾點:

  1. 爲了單元測試,要想辦法傳入配置,這樣就沒必要在文件系統中建立配置文件。
  2. 當設置應用時從藍圖調用一個函數,這樣就能夠有機會修改屬性(如掛接請求前/後 處理器等)。
  3. 若是有必要的話,當建立一個應用時增長一個 WSGI 中間件。

應用調度

應用調度是在 WSGI 層面組合多個 WSGI 應用的過程。能夠組合多個 Flask 應用,也能夠 組合 Flask 應用和其餘 WSGI 應用。經過這種組合,若是有必要的話,甚至能夠在同一個 解釋器中一邊運行 Django ,一邊運行 Flask 。這種組合的好處取決於應用內部是如何 工做的。

應用調度與 模塊化 的最大不一樣在於應用調度中的每一個 應用是徹底獨立的,它們以各自的配置運行,並在 WSGI 層面被調度。

說明

下面全部的技術說明和舉例都歸結於一個能夠運行於任何 WSGI 服務器的 application對象。對於生產環境,參見 部署方式 。對於開發環境, Werkzeug 提供了一個內建開發服務器,它使用 werkzeug.serving.run_simple() 來運行:

from werkzeug.serving import run_simple run_simple('localhost', 5000, application, use_reloader=True) 

注意 run_simple 不能用於生產環境,生產 環境服務器參見 成熟的 WSGI 服務器 。

爲了使用交互調試器,應用和簡單服務器都應當處於調試模式。下面是一個簡單的 「 hello world 」示例,使用了調試模式和 run_simple:

from flask import Flask from werkzeug.serving import run_simple app = Flask(__name__) app.debug = True @app.route('/') def hello_world(): return 'Hello World!' if __name__ == '__main__': run_simple('localhost', 5000, app, use_reloader=True, use_debugger=True, use_evalex=True) 
組合應用

若是你想在同一個 Python 解釋器中運行多個獨立的應用,那麼你能夠使用werkzeug.wsgi.DispatcherMiddleware 。其原理是:每一個獨立的 Flask 應用都 是一個合法的 WSGI 應用,它們經過調度中間件組合爲一個基於前綴調度的大應用。

假設你的主應用運行於 / ,後臺接口位於 /backend:

from werkzeug.wsgi import DispatcherMiddleware from frontend_app import application as frontend from backend_app import application as backend application = DispatcherMiddleware(frontend, { '/backend': backend }) 
根據子域調度

有時候你可能須要使用不一樣的配置來運行同一個應用的多個實例。能夠把應用建立過程 放在一個函數中,這樣調用這個函數就能夠建立一個應用的實例,具體實現參見 應用工廠 方案。

最多見的作法是每一個子域建立一個應用,配置服務器來調度全部子域的應用請求,使用 子域來建立用戶自定義的實例。一旦你的服務器能夠監聽全部子域,那麼就能夠使用一個 很簡單的 WSGI 應用來動態建立應用了。

WSGI 層是完美的抽象層,所以能夠寫一個你本身的 WSGI 應用來監視請求,並把請求分配 給你的 Flask 應用。若是被分配的應用尚未建立,那麼就會動態建立應用並被登記 下來:

from threading import Lock class SubdomainDispatcher(object): def __init__(self, domain, create_app): self.domain = domain self.create_app = create_app self.lock = Lock() self.instances = {} def get_application(self, host): host = host.split(':')[0] assert host.endswith(self.domain), 'Configuration error' subdomain = host[:-len(self.domain)].rstrip('.') with self.lock: app = self.instances.get(subdomain) if app is None: app = self.create_app(subdomain) self.instances[subdomain] = app return app def __call__(self, environ, start_response): app = self.get_application(environ['HTTP_HOST']) return app(environ, start_response) 

調度器示例:

from myapplication import create_app, get_user_for_subdomain from werkzeug.exceptions import NotFound def make_app(subdomain): user = get_user_for_subdomain(subdomain) if user is None: # 若是子域沒有對應的用戶,那麼仍是得返回一個 WSGI 應用 # 用於處理請求。這裏咱們把 NotFound() 異常做爲應用返回, # 它會被渲染爲一個缺省的 404 頁面。而後,可能還須要把 # 用戶重定向到主頁。 return NotFound() # 不然爲特定用戶建立應用 return create_app(user) application = SubdomainDispatcher('example.com', make_app) 
根據路徑調度

根據 URL 的路徑調度很是簡單。上面,咱們經過查找 Host 頭來判斷子域,如今 只要查找請求路徑的第一個斜槓以前的路徑就能夠了:

from threading import Lock from werkzeug.wsgi import pop_path_info, peek_path_info class PathDispatcher(object): def __init__(self, default_app, create_app): self.default_app = default_app self.create_app = create_app self.lock = Lock() self.instances = {} def get_application(self, prefix): with self.lock: app = self.instances.get(prefix) if app is None: app = self.create_app(prefix) if app is not None: self.instances[prefix] = app return app def __call__(self, environ, start_response): app = self.get_application(peek_path_info(environ)) if app is not None: pop_path_info(environ) else: app = self.default_app return app(environ, start_response) 

與根據子域調度相比最大的不一樣是:根據路徑調度時,若是建立函數返回 None ,那麼 就會回落到另外一個應用:

from myapplication import create_app, default_app, get_user_for_prefix def make_app(prefix): user = get_user_for_prefix(prefix) if user is not None: return create_app(user) application = PathDispatcher(default_app, make_app) 

實現 API 異常處理

在 Flask 上常常會執行 RESTful API 。開發者首先會遇到的問題之一是用於 API 的內建 異常處理不給力,回饋的內容不是頗有用。

對於非法使用 API ,比使用 abort 更好的解決方式是實現你本身的異常處理類型, 並安裝相應句柄,輸出符合用戶格式要求的出錯信息。

簡單的異常類

基本的思路是引入一個新的異常,回饋一個合適的可讀性高的信息、一個狀態碼和一些 可選的負載,給錯誤提供更多的環境內容。

如下是一個簡單的示例:

from flask import jsonify class InvalidUsage(Exception): status_code = 400 def __init__(self, message, status_code=None, payload=None): Exception.__init__(self) self.message = message if status_code is not None: self.status_code = status_code self.payload = payload def to_dict(self): rv = dict(self.payload or ()) rv['message'] = self.message return rv 

這樣一個視圖就能夠拋出帶有出錯信息的異常了。另外,還能夠經過 payload 參數以 字典的形式提供一些額外的負載。

註冊一個錯誤處理句柄

如今,視圖能夠拋出異常,可是會當即引起一個內部服務錯誤。這是由於沒有爲這個錯誤 處理類註冊句柄。句柄增長很容易,例如:

@app.errorhandler(InvalidAPIUsage) def handle_invalid_usage(error): response = jsonify(error.to_dict()) response.status_code = error.status_code return response 
在視圖中的用法

如下是如何在視圖中使用該功能:

@app.route('/foo') def get_foo(): raise InvalidUsage('This view is gone', status_code=410) 

URL 處理器

New in version 0.7.

Flask 0.7 引入了 URL 處理器,其做用是爲你處理大量包含相同部分的 URL 。假設你有 許多 URL 都包含語言代碼,可是又不想在每一個函數中都重複處理這個語言代碼,那麼就可 能夠使用 URL 處理器。

在與藍圖配合使用時, URL 處理器格外有用。下面咱們分別演示在應用中和藍圖中使用 URL 處理器。

國際化應用的 URL

假設有應用以下:

from flask import Flask, g app = Flask(__name__) @app.route('/<lang_code>/') def index(lang_code): g.lang_code = lang_code ... @app.route('/<lang_code>/about') def about(lang_code): g.lang_code = lang_code ... 

上例中出現了大量的重複:必須在每個函數中把語言代碼賦值給 g 對象。固然,若是使用一個裝飾器能夠簡化這個工做。可是,當你須要生成由一個函數 指向另外一個函數的 URL 時,仍是得顯式地提供語言代碼,至關麻煩。

咱們使用 url_defaults() 函數來簡化這個問題。這個函數能夠自動 把值注入到url_for() 。如下代碼檢查在 URL 字典中是否存在語言代碼, 端點是否須要一個名爲'lang_code' 的值:

@app.url_defaults
def add_language_code(endpoint, values): if 'lang_code' in values or not g.lang_code: return if app.url_map.is_endpoint_expecting(endpoint, 'lang_code'): values['lang_code'] = g.lang_code 

URL 映射的 is_endpoint_expecting() 方法可用於檢查 端點是否須要提供一個語言代碼。

上例的逆向函數是 url_value_preprocessor() 。這些函數在請求 匹配後當即根據 URL 的值執行代碼。它們能夠從 URL 字典中取出值,並把取出的值放在 其餘地方:

@app.url_value_preprocessor
def pull_lang_code(endpoint, values): g.lang_code = values.pop('lang_code', None) 

這樣就沒必要在每一個函數中把 lang_code 賦值給 g 了。你還能夠做 進一步改進:寫一個裝飾器把語言代碼做爲 URL 的前綴。可是更好的解決方式是使用 藍圖。一旦 'lang_code'從值的字典中彈出,它就再也不傳送給視圖函數了。精簡後的 代碼以下:

from flask import Flask, g app = Flask(__name__) @app.url_defaults def add_language_code(endpoint, values): if 'lang_code' in values or not g.lang_code: return if app.url_map.is_endpoint_expecting(endpoint, 'lang_code'): values['lang_code'] = g.lang_code @app.url_value_preprocessor def pull_lang_code(endpoint, values): g.lang_code = values.pop('lang_code', None) @app.route('/<lang_code>/') def index(): ... @app.route('/<lang_code>/about') def about(): ... 
國際化的藍圖 URL

由於藍圖能夠自動給全部 URL 加上一個統一的前綴,因此應用到每一個函數就很是方便了。 更進一步,由於藍圖 URL 預處理器不須要檢查 URL 是否真的須要要一個'lang_code' 參數,因此能夠去除 url_defaults() 函數中的 邏輯判斷:

from flask import Blueprint, g bp = Blueprint('frontend', __name__, url_prefix='/<lang_code>') @bp.url_defaults def add_language_code(endpoint, values): values.setdefault('lang_code', g.lang_code) @bp.url_value_preprocessor def pull_lang_code(endpoint, values): g.lang_code = values.pop('lang_code') @bp.route('/') def index(): ... @bp.route('/about') def about(): ... 

使用 Distribute 部署

distribute 的前身是 setuptools ,它是一個擴展庫,一般用於分發 Python 庫和 擴展。它的英文名稱的就是「分發」的意思。它擴展了 Python 自帶的一個基礎模塊安裝 系統 distutils ,支持多種更復雜的結構,方便了大型應用的分發部署。它的主要特點:

  • 支持依賴 :一個庫或者應用能夠聲明其所依賴的其餘庫的列表。依賴庫將被自動 安裝。
  • 包註冊 :能夠在安裝過程當中註冊包,這樣就能夠經過一個包查詢其餘包的信息。 這套系統最有名的功能是「切入點」,即一個包能夠定義一個入口,以便於其餘包掛接, 用以擴展包。
  • 安裝管理 : distribute 中的 easy_install 能夠爲你安裝其餘庫。你也能夠 使用遲早會替代 easy_install 的 pip ,它除了安裝包還能夠作更多的事。

Flask 自己,以及其餘全部在 cheeseshop 中能夠找到的庫要麼是用 distribute 分發的, 要麼是用老的 setuptools 或 distutils 分發的。

在這裏咱們假設你的應用名稱是 yourapplication.py ,且沒有使用模塊,而是一個  。distribute 不支持分發標準模塊,所以咱們不 討論模塊的問題。關於如何把模塊轉換爲包的信息參見 大型應用 方案。

使用 distribute 將使發佈更復雜,也更加自動化。若是你想要徹底自動化處理,請同時 閱讀 使用 Fabric 部署 一節。

基礎設置腳本

由於你已經安裝了 Flask ,因此你應當已經安裝了 setuptools 或 distribute 。若是 沒有安裝,不用怕,有一個 distribute_setup.py 腳本能夠幫助你安裝。只要下載這個 腳本,並在你的 Python 解釋器中運行就能夠了。

標準聲明: 最好使用 virtualenv 。

你的設置代碼應用放在 setup.py 文件中,這個文件應當位於應用旁邊。這個文件名只是 一個約定,可是最好不要改變,由於你們都會去找這個文件。

是的,即便你使用 distribute ,你導入的包也是 setuptools 。 distribute 徹底 向後兼容於 setuptools ,所以它使用相同的導入名稱。

Flask 應用的基礎 setup.py 文件示例以下:

from setuptools import setup setup( name='Your Application', version='1.0', long_description=__doc__, packages=['yourapplication'], include_package_data=True, zip_safe=False, install_requires=['Flask'] ) 

請記住,你必須顯式的列出子包。若是你要 distribute 自動爲你搜索包,你能夠使用find_packages 函數:

from setuptools import setup, find_packages

setup(
    ...
    packages=find_packages()
)

大多數 setup 的參數能夠望文生義,可是 include_package_data 和 zip_safe 能夠不容易理解。 include_package_data 告訴 distribute 要搜索一個 MANIFEST.in 文件,把文件內容所匹配的全部條目做爲包數據安裝。能夠經過使用這個 參數分發 Python 模塊的靜態文件和模板(參見 分發資源 )。 zip_safe 標誌可用於強制或防止建立 zip 壓縮包。一般你不會想要把包安裝爲 zip 壓縮文件,由於一些工具不支持壓縮文件,並且壓縮文件比較難以調試。

分發資源

若是你嘗試安裝上文建立的包,你會發現諸如 static 或 templates 之類的文件夾 沒有被安裝。緣由是 distribute 不知道要爲你添加哪些文件。你要作的是:在你的 setup.py 文件旁邊建立一個 MANIFEST.in 文件。這個文件列出了全部應當添加到 tar 壓縮包的文件:

recursive-include yourapplication/templates *
recursive-include yourapplication/static *

不要忘了把 setup 函數的 include_package_data 參數設置爲 True !不然即便把 內容在MANIFEST.in 文件中所有列出來也沒有用。

聲明依賴

依賴是在 install_requires 參數中聲明的,這個參數是一個列表。列表中的每一項都是 一個須要在安裝時從 PyPI 得到的包。缺省狀況下,老是會得到最新版本的包,但你能夠 指定最高版本和最低版本。示例:

install_requires=[ 'Flask>=0.2', 'SQLAlchemy>=0.6', 'BrokenPackage>=0.7,<=1.0' ] 

我前面提到,依賴包都從 PyPI 得到的。可是若是要從別的地方得到包怎麼辦呢?你只要 仍是按照上述方法寫,而後提供一個可選地址列表就好了:

dependency_links=['http://example.com/yourfiles'] 

請確保頁面上有一個目錄列表,且頁面上的連接指向正確的 tar 壓縮包。這樣 distribute 就會找到文件了。若是你的包在公司內部網絡上,請提供指向服務器的 URL 。

安裝 / 開發

要安裝你的應用(理想狀況下是安裝到一個 virtualenv ),只要運行帶 install 參數 的setup.py 腳本就能夠了。它會將你的應用安裝到 virtualenv 的 site-packages 文件夾下,同時下載並安裝依賴:

$ python setup.py install

若是你正開發這個包,同時也但願相關依賴被安裝,那麼能夠使用 develop 來代替:

$ python setup.py develop

這樣作的好處是隻安裝一個指向 site-packages 的鏈接,而不是把數據複製到那裏。這樣 在開發過程當中就沒必要每次修改之後再運行 install 了。

使用 Fabric 部署

Fabric 是一個 Python 工具,與 Makefiles 相似,可是可以在遠程服務器上執行 命令。若是與適當的 Python 包( 大型應用 )與優良的配置( 配置管理 )相結合那麼 Fabric 將是在外部服務器上部署 Flask 的利器。

在下文開始以前,有幾點須要明確:

  • Fabric 1.0 須要要被安裝到本地。本教程假設使用的是最新版本的 Fabric 。
  • 應用已是一個包,且有一個可用的 setup.py 文件( 使用 Distribute 部署 )。
  • 在下面的例子中,咱們假設遠程服務器使用 mod_wsgi 。固然,你能夠使用你本身 喜歡的服務器,可是在示例中咱們選擇 Apache + mod_wsgi ,由於它們設置方便, 且在沒有 root 權限狀況下能夠方便的重載應用。
建立第一個 Fabfile

fabfile 是控制 Fabric 的東西,其文件名爲 fabfile.py ,由 fab 命令執行。在 這個文件中定義的全部函數都會被視做 fab 子命令。這些命令將會在一個或多個主機上 運行。這些主機能夠在 fabfile 中定義,也能夠在命令行中定義。本例將在 fabfile 中 定義主機。

下面是第一個例子,比較級。它能夠把當前的源代碼上傳至服務器,並安裝到一個預先存在 的 virtual 環境:

from fabric.api import * # 使用遠程命令的用戶名 env.user = 'appuser' # 執行命令的服務器 env.hosts = ['server1.example.com', 'server2.example.com'] def pack(): # 建立一個新的分發源,格式爲 tar 壓縮包 local('python setup.py sdist --formats=gztar', capture=False) def deploy(): # 定義分發版本的名稱和版本號 dist = local('python setup.py --fullname', capture=True).strip() # 把 tar 壓縮包格式的源代碼上傳到服務器的臨時文件夾 put('dist/%s.tar.gz' % dist, '/tmp/yourapplication.tar.gz') # 建立一個用於解壓縮的文件夾,並進入該文件夾 run('mkdir /tmp/yourapplication') with cd('/tmp/yourapplication'): run('tar xzf /tmp/yourapplication.tar.gz') # 如今使用 virtual 環境的 Python 解釋器來安裝包 run('/var/www/yourapplication/env/bin/python setup.py install') # 安裝完成,刪除文件夾 run('rm -rf /tmp/yourapplication /tmp/yourapplication.tar.gz') # 最後 touch .wsgi 文件,讓 mod_wsgi 觸發應用重載 run('touch /var/www/yourapplication.wsgi') 

上例中的註釋詳細,應當是容易理解的。如下是 fabric 提供的最經常使用命令的簡要說明:

  • run - 在遠程服務器上執行一個命令
  • local - 在本地機器上執行一個命令
  • put - 上傳文件到遠程服務器上
  • cd - 在服務器端改變目錄。必須與 with 語句聯合使用。
運行 Fabfile

那麼如何運行 fabfile 呢?答案是使用 fab 命令。要在遠程服務器上部署當前版本的 代碼能夠使用這個命令:

$ fab pack deploy

可是這個命令須要遠程服務器上已經建立了 /var/www/yourapplication 文件夾,且/var/www/yourapplication/env 是一個 virtual 環境。更進一步,服務器上尚未 建立配置文件和 .wsgi 文件。那麼,咱們如何在一個新的服務器上建立一個基礎環境 呢?

這個問題取決於你要設置多少臺服務器。若是隻有一臺應用服務器(多數狀況下),那麼 在 fabfile 中建立命令有一點多餘。固然,你能夠這麼作。這個命令能夠稱之爲 setup或 bootstrap 。在使用命令時顯式傳遞服務器名稱:

$ fab -H newserver.example.com bootstrap

設置一個新服務器大體有如下幾個步驟:

  1. 在 /var/www 建立目錄結構:

    $ mkdir /var/www/yourapplication
    $ cd /var/www/yourapplication
    $ virtualenv --distribute env
    
  2. 上傳一個新的 application.wsgi 文件和應用配置文件(如 application.cfg ) 到服務器上。

  3. 建立一個新的用於 yourapplication 的 Apache 配置並激活它。要確保激活 .wsgi 文件變更監視,這樣在 touch 的時候能夠自動重載應用。( 更多信息參見 mod_wsgi (Apache) )

如今的問題是: application.wsgi 和 application.cfg 文件從哪裏來?

WSGI 文件

WSGI 文件必須導入應用,而且還必須設置一個環境變量用於告訴應用到哪裏去搜索配置。 示例:

import os os.environ['YOURAPPLICATION_CONFIG'] = '/var/www/yourapplication/application.cfg' from yourapplication import app 

應用自己必須像下面這樣初始化本身才會根據環境變量搜索配置:

app = Flask(__name__) app.config.from_object('yourapplication.default_config') app.config.from_envvar('YOURAPPLICATION_CONFIG') 

這個方法在 配置管理 一節已做了詳細的介紹。

配置文件

上文已談到,應用會根據 YOURAPPLICATION_CONFIG 環境變量找到正確的配置文件。 所以咱們應當把配置文件放在應用能夠找到的地方。在不一樣的電腦上配置文件是不一樣的, 因此通常咱們不對配置文件做版本處理。

一個流行的方法是在一個獨立的版本控制倉庫爲不一樣的服務器保存不一樣的配置文件,而後 在全部服務器進行檢出。而後在須要的地方使用配置文件的符號連接(例如:/var/www/yourapplication )。

無論如何,咱們這裏只有一到兩臺服務器,所以咱們能夠預先手動上傳配置文件。

第一次部署

如今咱們能夠進行第一次部署了。我已經設置好了服務器,所以服務器上應當已經有了 virtual 環境和已激活的 apache 配置。如今咱們能夠打包應用並部署它了:

$ fab pack deploy

Fabric 如今會鏈接全部服務器並運行 fabfile 中的全部命令。首先它會打包應用獲得一個 tar 壓縮包。而後會執行分發,把源代碼上傳到全部服務器並安裝。感謝 setup.py 文件,所須要的依賴庫會自動安裝到 virtual 環境。

下一步

在前文的基礎上,還有更多的方法能夠所有署工做更加輕鬆:

  • 建立一個初始化新服務器的 bootstrap 命令。它能夠初始化一個新的 virtual 環境、正確設置 apache 等等。
  • 把配置文件放入一個獨立的版本庫中,把活動配置的符號連接放在適當的地方。
  • 還能夠把應用代碼放在一個版本庫中,在服務器上檢出最新版本後安裝。這樣你能夠 方便的回滾到老版本。
  • 掛接測試功能,方便部署到外部服務器進行測試。

使用 Fabric 是一件有趣的事情。你會發如今電腦上打出 fab deploy 是很是神奇的。 你能夠看到你的應用被部署到一個又一個服務器上。

在 Flask 中使用 SQLite 3

在 Flask 中,你能夠方便的按需打開數據庫鏈接,而且在環境解散時關閉這個鏈接( 一般是請求結束的時候)。

如下是一個在 Flask 中使用 SQLite 3 的例子:

import sqlite3 from flask import g DATABASE = '/path/to/database.db' def get_db(): db = getattr(g, '_database', None) if db is None: db = g._database = connect_to_database() return db @app.teardown_appcontext def close_connection(exception): db = getattr(g, '_database', None) if db is not None: db.close() 

爲了使用數據庫,全部應用都必須準備好一個處於激活狀態的環境。使用 get_db 函數能夠獲得數據庫鏈接。當環境解散時,數據庫鏈接會被切斷。

注意:若是你使用的是 Flask 0.9 或者之前的版本,那麼你必須使用flask._app_ctx_stack.top ,而不是 g 。由於 flask.g 對象是綁定到 請求的,而不是應用環境。

示例:

@app.route('/') def index(): cur = get_db().cursor() ... 

Note

請記住,解散請求和應用環境的函數是必定會被執行的。即便請求前處理器執行失敗或 根本沒有執行,解散函數也會被執行。所以,咱們必須保證在關閉數據庫鏈接以前 數據庫鏈接是存在的。

按需鏈接

上述方式(在第一次使用時鏈接數據庫)的優勢是隻有在真正須要時纔打開數據庫鏈接。 若是你想要在一個請求環境以外使用數據庫鏈接,那麼你能夠手動在 Python 解釋器打開 應用環境:

with app.app_context():
    # now you can use get_db()
簡化查詢

如今,在每一個請求處理函數中能夠經過訪問 g.db 來獲得當前打開的數據庫鏈接。爲了 簡化 SQLite 的使用,這裏有一個有用的行工廠函數。該函數會轉換每次從數據庫返回的 結果。例如,爲了獲得字典類型而不是元組類型的返回結果,能夠這樣:

def make_dicts(cursor, row): return dict((cur.description[idx][0], value) for idx, value in enumerate(row)) db.row_factory = make_dicts 

或者更簡單的:

db.row_factory = sqlite3.Row 

此外,把獲得遊標,執行查詢和得到結果組合成一個查詢函數不失爲一個好辦法:

def query_db(query, args=(), one=False): cur = get_db().execute(query, args) rv = cur.fetchall() cur.close() return (rv[0] if rv else None) if one else rv 

上述的方便的小函數與行工廠聯合使用與使用原始的數據庫遊標和鏈接相比要方便多了。

能夠這樣使用上述函數:

for user in query_db('select * from users'): print user['username'], 'has the id', user['user_id'] 

只須要獲得單一結果的用法:

user = query_db('select * from users where username = ?', [the_username], one=True) if user is None: print 'No such user' else: print the_username, 'has the id', user['user_id'] 

若是要給 SQL 語句傳遞參數,請在語句中使用問號來代替參數,並把參數放在一個列表中 一塊兒傳遞。不要用字符串格式化的方式直接把參數加入 SQL 語句中,這樣會給應用帶來 SQL 注入 的風險。

初始化模式

關係數據庫是須要模式的,所以一個應用經常須要一個 schema.sql 文件來建立 數據庫。所以咱們須要使用一個函數來其於模式建立數據庫。下面這個函數能夠完成這個 任務:

def init_db(): with app.app_context(): db = get_db() with app.open_resource('schema.sql', mode='r') as f: db.cursor().executescript(f.read()) db.commit() 

能夠使用上述函數在 Python 解釋器中建立數據庫:

>>> from yourapplication import init_db >>> init_db() 

在 Flask 中使用 SQLAlchemy

許多人喜歡使用 SQLAlchemy 來訪問數據庫。建議在你的 Flask 應用中使用包來代替 模塊,並把模型放入一個獨立的模塊中(參見 大型應用 )。雖然這 不是必須的,可是頗有用。

有四種 SQLAlchemy 的經常使用方法,下面一一道來:

Flask-SQLAlchemy 擴展

由於 SQLAlchemy 是一個經常使用的數據庫抽象層,而且須要必定的配置才能使用,所以咱們 爲你作了一個處理 SQLAlchemy 的擴展。若是你須要快速的開始使用 SQLAlchemy ,那麼 推薦你使用這個擴展。

你能夠從 PyPI 下載 Flask-SQLAlchemy 。

聲明

SQLAlchemy 中的聲明擴展是使用 SQLAlchemy 的最新方法,它容許你像 Django 同樣, 在一個地方定義表和模型而後處處使用。除了如下內容,我建議你閱讀 聲明 的官方 文檔。

如下是示例 database.py 模塊:

from sqlalchemy import create_engine from sqlalchemy.orm import scoped_session, sessionmaker from sqlalchemy.ext.declarative import declarative_base engine = create_engine('sqlite:////tmp/test.db', convert_unicode=True) db_session = scoped_session(sessionmaker(autocommit=False, autoflush=False, bind=engine)) Base = declarative_base() Base.query = db_session.query_property() def init_db(): # 在這裏導入定義模型所須要的全部模塊,這樣它們就會正確的註冊在 # 元數據上。不然你就必須在調用 init_db() 以前導入它們。 import yourapplication.models Base.metadata.create_all(bind=engine) 

要定義模型的話,只要繼承上面建立的 Base 類就能夠了。你可能會奇怪這裏爲何 不用理會線程(就像咱們在 SQLite3 的例子中同樣使用 g 對象)。 緣由是 SQLAlchemy 已經用 scoped_session 爲咱們作好了此 類工做。

若是要在應用中以聲明方式使用 SQLAlchemy ,那麼只要把下列代碼加入應用模塊就能夠 了。 Flask 會自動在請求結束時或者應用關閉時刪除數據庫會話:

from yourapplication.database import db_session @app.teardown_appcontext def shutdown_session(exception=None): db_session.remove() 

如下是一個示例模型(放入 models.py 中):

from sqlalchemy import Column, Integer, String from yourapplication.database import Base class User(Base): __tablename__ = 'users' id = Column(Integer, primary_key=True) name = Column(String(50), unique=True) email = Column(String(120), unique=True) def __init__(self, name=None, email=None): self.name = name self.email = email def __repr__(self): return '<User %r>' % (self.name) 

能夠使用 init_db 函數來建立數據庫:

>>> from yourapplication.database import init_db >>> init_db() 

在數據庫中插入條目示例:

>>> from yourapplication.database import db_session >>> from yourapplication.models import User >>> u = User('admin', 'admin@localhost') >>> db_session.add(u) >>> db_session.commit() 

查詢很簡單:

>>> User.query.all() [<User u'admin'>] >>> User.query.filter(User.name == 'admin').first() <User u'admin'> 
人工對象關係映射

人工對象關係映射相較於上面的聲明方式有優勢也有缺點。主要區別是人工對象關係映射 分別定義表和類並映射它們。這種方式更靈活,可是要多些代碼。一般,這種方式與聲明 方式同樣運行,所以請確保把你的應用在包中分爲多個模塊。

示例 database.py 模塊:

from sqlalchemy import create_engine, MetaData from sqlalchemy.orm import scoped_session, sessionmaker engine = create_engine('sqlite:////tmp/test.db', convert_unicode=True) metadata = MetaData() db_session = scoped_session(sessionmaker(autocommit=False, autoflush=False, bind=engine)) def init_db(): metadata.create_all(bind=engine) 

就像聲明方法同樣,你須要在請求後或者應用環境解散後關閉會話。把如下代碼放入你的 應用模塊:

from yourapplication.database import db_session @app.teardown_appcontext def shutdown_session(exception=None): db_session.remove() 

如下是一個示例表和模型(放入 models.py 中):

from sqlalchemy import Table, Column, Integer, String from sqlalchemy.orm import mapper from yourapplication.database import metadata, db_session class User(object): query = db_session.query_property() def __init__(self, name=None, email=None): self.name = name self.email = email def __repr__(self): return '<User %r>' % (self.name) users = Table('users', metadata, Column('id', Integer, primary_key=True), Column('name', String(50), unique=True), Column('email', String(120), unique=True) ) mapper(User, users) 

查詢和插入與聲明方式的同樣。

SQL 抽象層

若是你只須要使用數據庫系統(和 SQL )抽象層,那麼基本上只要使用引擎:

from sqlalchemy import create_engine, MetaData engine = create_engine('sqlite:////tmp/test.db', convert_unicode=True) metadata = MetaData(bind=engine) 

而後你要麼像前文中同樣在代碼中聲明表,要麼自動載入它們:

users = Table('users', metadata, autoload=True) 

能夠使用 insert 方法插入數據。爲了使用事務,咱們必須先獲得一個鏈接:

>>> con = engine.connect() >>> con.execute(users.insert(), name='admin', email='admin@localhost') 

SQLAlchemy 會自動提交。

能夠直接使用引擎或鏈接來查詢數據庫:

>>> users.select(users.c.id == 1).execute().first() (1, u'admin', u'admin@localhost') 

查詢結果也是類字典元組:

>>> r = users.select(users.c.id == 1).execute().first() >>> r['name'] u'admin' 

你也能夠把 SQL 語句做爲字符串傳遞給 execute() 方法:

>>> engine.execute('select * from users where id = :1', [1]).first() (1, u'admin', u'admin@localhost') 

關於 SQLAlchemy 的更多信息請移步其 官方網站 。

上傳文件

是的,這裏要談的是一個老問題:文件上傳。文件上傳的基本原理實際上很簡單,基本上 是:

  1. 一個帶有 enctype=multipart/form-data 的 <form> 標記,標記中含有 一個 <inputtype=file> 。
  2. 應用經過請求對象的 files 字典來訪問文件。
  3. 使用文件的 save() 方法把文件永久 地保存在文件系統中。
簡介

讓咱們從一個基本的應用開始,這個應用上傳文件到一個指定目錄,並把文件顯示給用戶。 如下是應用的引導代碼:

import os from flask import Flask, request, redirect, url_for from werkzeug import secure_filename UPLOAD_FOLDER = '/path/to/the/uploads' ALLOWED_EXTENSIONS = set(['txt', 'pdf', 'png', 'jpg', 'jpeg', 'gif']) app = Flask(__name__) app.config['UPLOAD_FOLDER'] = UPLOAD_FOLDER 

首先咱們導入了一堆東西,大多數是淺顯易懂的。werkzeug.secure_filename() 會 在稍後解釋。UPLOAD_FOLDER 是上傳文件要儲存的目錄,ALLOWED_EXTENSIONS 是容許 上傳的文件擴展名的集合。接着咱們給應用手動添加了一個 URL 規則。通常如今不會作 這個,可是爲何這麼作了呢?緣由是咱們須要服務器(或咱們的開發服務器)爲咱們提供 服務。所以咱們只生成這些文件的 URL 的規則。

爲何要限制文件件的擴展名呢?若是直接向客戶端發送數據,那麼你可能不會想讓用戶 上傳任意文件。不然,你必須確保用戶不能上傳 HTML 文件,由於 HTML 可能引發 XSS 問題(參見 跨站腳本攻擊(XSS) )。若是服務器能夠執行 PHP 文件,那麼還必須確保不容許上傳 .php 文件。可是誰又會在服務器上安裝 PHP 呢,對不? :)

下一個函數檢查擴展名是否合法,上傳文件,把用戶重定向到已上傳文件的 URL:

def allowed_file(filename): return '.' in filename and \ filename.rsplit('.', 1)[1] in ALLOWED_EXTENSIONS @app.route('/', methods=['GET', 'POST']) def upload_file(): if request.method == 'POST': file = request.files['file'] if file and allowed_file(file.filename): filename = secure_filename(file.filename) file.save(os.path.join(app.config['UPLOAD_FOLDER'], filename)) return redirect(url_for('uploaded_file', filename=filename)) return '''  <!doctype html>  <title>Upload new File</title>  <h1>Upload new File</h1>  <form action="" method=post enctype=multipart/form-data>  <p><input type=file name=file>  <input type=submit value=Upload>  </form>  ''' 

那麼 secure_filename() 函數究竟是有什麼用?有一條原則是「 永遠不要信任用戶輸入」。這條原則一樣適用於已上傳文件的文件名。全部提交的表單數據 多是僞造的,文件名也能夠是危險的。此時要謹記:在把文件保存到文件系統以前老是要 使用這個函數對文件名進行安檢。

進一步說明

你能夠會好奇 secure_filename() 作了哪些工做,若是不使用 它會有什麼後果。假設有人把下面的信息做爲 filename 傳遞給你的應用:

filename = "../../../../home/username/.bashrc" 

假設 ../ 的個數是正確的,你會把它和 UPLOAD_FOLDER 結合在一塊兒,那麼用戶 就可能有能力修改一個服務器上的文件,這個文件原本是用戶無權修改的。這須要瞭解 應用是如何運行的,可是請相信我,黑客都是病態的 :)

如今來看看函數是如何工做的:

>>> secure_filename('../../../../home/username/.bashrc') 'home_username_.bashrc' 

如今還剩下一件事:爲已上傳的文件提供服務。 Flask 0.5 版本開始咱們能夠使用一個 函數來完成這個任務:

from flask import send_from_directory @app.route('/uploads/<filename>') def uploaded_file(filename): return send_from_directory(app.config['UPLOAD_FOLDER'], filename) 

另外,能夠把 uploaded_file 註冊爲 build_only 規則,並使用SharedDataMiddleware 。這種方式能夠在 Flask 老版本中 使用:

from werkzeug import SharedDataMiddleware app.add_url_rule('/uploads/<filename>', 'uploaded_file', build_only=True) app.wsgi_app = SharedDataMiddleware(app.wsgi_app, { '/uploads': app.config['UPLOAD_FOLDER'] }) 

若是你如今運行應用,那麼應該一切都應該按預期正常工做。

改進上傳

New in version 0.6.

Flask 究竟是如何處理文件上傳的呢?若是上傳的文件很小,那麼會把它們儲存在內存中。 不然就會把它們保存到一個臨時的位置(經過 tempfile.gettempdir() 能夠獲得 這個位置)。可是,如何限制上傳文件的尺寸呢?缺省狀況下, Flask 是不限制上傳文件 的尺寸的。能夠經過設置配置的 MAX_CONTENT_LENGTH 來限制文件尺寸:

from flask import Flask, Request app = Flask(__name__) app.config['MAX_CONTENT_LENGTH'] = 16 * 1024 * 1024 

上面的代碼會把尺寸限制爲 16 M 。若是上傳了大於這個尺寸的文件, Flask 會拋出一個RequestEntityTooLarge 異常。

Flask 0.6 版本中添加了這個功能。可是經過繼承請求對象,在較老的版本中也能夠實現 這個功能。更多信息請參閱 Werkzeug 關於文件處理的文檔。

上傳進度條

在不久之前,許多開發者是這樣實現上傳進度條的:分塊讀取上傳的文件,在數據庫中儲存 上傳的進度,而後在客戶端經過 JavaScript 獲取進度。簡而言之,客戶端每 5 秒鐘向 服務器詢問一次上傳進度。以爲諷刺嗎?客戶端在明知故問。

如今有了更好的解決方案,更快且更可靠。網絡發生了很大變化,你能夠在客戶端使用 HTML5 、 JAVA 、 Silverlight 或 Flash 得到更好的上傳體驗。請查看如下庫,學習 一些優秀的上傳的示例:

一個更簡便的方案

由於全部應用中上傳文件的方案基本相同,所以能夠使用 Flask-Uploads 擴展來實現 文件上傳。這個擴展實現了完整的上傳機制,還具備白名單功能、黑名單功能以及其餘 功能。

緩存

當你的應用變慢的時候,能夠考慮加入緩存。至少這是最簡單的加速方法。緩存有什麼用? 假設有一個函數耗時較長,可是這個函數在五分鐘前返回的結果仍是正確的。那麼咱們就 能夠考慮把這個函數的結果在緩存中存放一段時間。

Flask 自己不提供緩存,可是它的基礎庫之一 Werkzeug 有一些很是基本的緩存支持。它 支持多種緩存後端,一般你須要使用的是 memcached 服務器。

設置一個緩存

與建立 Flask 對象相似,你須要建立一個緩存對象並保持它。若是你 正在使用開發服務器,那麼你能夠建立一個 SimpleCache 對象。這個對象提供簡單的緩存,它把 緩存項目保存在 Python 解釋器的內存中:

from werkzeug.contrib.cache import SimpleCache cache = SimpleCache() 

若是你要使用 memcached ,那麼請確保有 memcache 模塊支持(你能夠從 PyPI 得到模塊)和一個正在運行的 memcached 服務器。 鏈接 memcached 服務器示例:

from werkzeug.contrib.cache import MemcachedCache cache = MemcachedCache(['127.0.0.1:11211']) 

若是你正在使用 App Engine ,那麼你能夠方便地鏈接到 App Engine memcache 服務器:

from werkzeug.contrib.cache import GAEMemcachedCache cache = GAEMemcachedCache() 
使用緩存

如今的問題是如何使用緩存呢?有兩個很是重要的操做: get() 和 set() 。下面展現如何使用它們:

get() 用於從緩存中得到項目,調用時使用 一個字符串做爲鍵名。若是項目存在,那麼就會返回這個項目,不然返回 None:

rv = cache.get('my-item') 

set() 用於把項目添加到緩存中。第一個參數 是鍵名,第二個參數是鍵值。還能夠提供一個超時參數,當超過期間後項目會自動刪除。

下面是一個完整的例子:

def get_my_item(): rv = cache.get('my-item') if rv is None: rv = calculate_value() cache.set('my-item', rv, timeout=5 * 60) return rv 

視圖裝飾器

Python 有一個很是有趣的功能:函數裝飾器。這個功能能夠使網絡應用乾淨整潔。 Flask 中的每一個視圖都是一個裝飾器,它能夠被注入額外的功能。你能夠已經用過了route() 裝飾器。可是,你有可能須要使用你本身的裝飾器。假設有 一個視圖,只有已經登陸的用戶才能使用。若是用戶訪問時沒有登陸,則會被重定向到 登陸頁面。這種狀況下就是使用裝飾器的絕佳機會。

檢查登陸裝飾器

讓咱們來實現這個裝飾器。裝飾器是一個返回函數的函數。聽上去複雜,其實很簡單。只要 記住一件事:裝飾器用於更新函數的 __name__ 、 __module__ 和其餘屬性。這一點很 容易忘記,可是你沒必要人工更新函數屬性,能夠使用一個相似於裝飾器的函數(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 
緩存裝飾器

假設有一個視圖函數須要消耗昂貴的計算成本,所以你須要在必定時間內緩存這個視圖的 計算結果。這種狀況下裝飾器是一個好的選擇。咱們假設你像 緩存 方案中同樣設置了緩存。

下面是一個示例緩存函數。它根據一個特定的前綴(其實是一個格式字符串)和請求的 當前路徑生成緩存鍵。注意,咱們先使用了一個函數來建立裝飾器,這個裝飾器用於裝飾 函數。聽起來拗口吧,確實有一點複雜,可是下面的示例代碼仍是很容易讀懂的。

被裝飾代碼按以下步驟工做

  1. 基於基礎路徑得到當前請求的惟一緩存鍵。
  2. 從緩存中獲取鍵值。若是獲取成功則返回獲取到的值。
  3. 不然調用原來的函數,並把返回值存放在緩存中,直至過時(缺省值爲五分鐘)。

代碼:

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 

注意,以上代碼假設存在一個可用的實例化的 cache 對象,更多信息參見 緩存 方案。

模板裝飾器

不久前, TurboGear 的人發明了模板裝飾器這個通用模式。其工做原理是返回一個字典, 這個字典包含從視圖傳遞給模板的值,模板自動被渲染。如下三個例子的功能是相同的:

@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 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 路由系統,以便於得到更強的靈活性時,須要和 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" 

使用 WTForms 進行表單驗證

當你必須處理瀏覽器提交的表單數據時,視圖代碼很快會變得難以閱讀。有一些庫能夠 簡化這個工做,其中之一即是 WTForms ,下面咱們將介紹這個庫。若是你必須處理 許多表單,那麼應當嘗試使用這個庫。

若是要使用 WTForms ,那麼首先要把表單定義爲類。我推薦把應用分割爲多個模塊(大型應用 ),併爲表單添加一個獨立的模塊。

使用一個擴展得到大部分 WTForms 的功能

Flask-WTF 擴展能夠實現本方案的全部功能,而且還提供一些小的輔助工具。使用 這個擴展能夠更好的使用表單和 Flask 。你能夠從 PyPI 得到這個擴展。

表單

下面是一個典型的註冊頁面的示例:

from wtforms import Form, BooleanField, TextField, PasswordField, validators class RegistrationForm(Form): username = TextField('Username', [validators.Length(min=4, max=25)]) email = TextField('Email Address', [validators.Length(min=6, max=35)]) password = PasswordField('New Password', [ validators.Required(), validators.EqualTo('confirm', message='Passwords must match') ]) confirm = PasswordField('Repeat Password') accept_tos = BooleanField('I accept the TOS', [validators.Required()]) 
視圖

在視圖函數中,表單用法示例以下:

@app.route('/register', methods=['GET', 'POST']) def register(): form = RegistrationForm(request.form) if request.method == 'POST' and form.validate(): user = User(form.username.data, form.email.data, form.password.data) db_session.add(user) flash('Thanks for registering') return redirect(url_for('login')) return render_template('register.html', form=form) 

注意,這裏咱們默認視圖使用了 SQLAlchemy ( 在 Flask 中使用 SQLAlchemy ),固然這 不是必須的。請根據你的實際狀況修改代碼。

請記住如下幾點:

  1. 若是數據是經過 HTTP POST 方法提交的,請根據 form 的 值建立表單。若是是經過GET 方法提交的,則相應的是 args 。
  2. 調用 validate() 函數來驗證數據。若是驗證經過,則 函數返回 True ,不然返回False 。
  3. 經過 form.<NAME>.data 能夠訪問表單中單個值。
模板中的表單

如今咱們來看看模板。把表單傳遞給模板後就能夠輕鬆渲染它們了。看一看下面的示例 模板就能夠知道有多輕鬆了。 WTForms 替咱們完成了一半表單生成工做。爲了作得更好, 咱們能夠寫一個宏,經過這個宏渲染帶有一個標籤的字段和錯誤列表(若是有的話)。

如下是一個使用宏的示例 _formhelpers.html 模板:

{% macro render_field(field) %} <dt>{{ field.label }} <dd>{{ field(**kwargs)|safe }} {% if field.errors %} <ul class=errors> {% for error in field.errors %} <li>{{ error }}</li> {% endfor %} </ul> {% endif %} </dd> {% endmacro %} 

上例中的宏接受一堆傳遞給 WTForm 字段函數的參數,爲咱們渲染字段。參數會做爲 HTML 屬性插入。例如你能夠調用 render_field(form.username,class='username') 來 爲輸入元素添加一個類。注意: WTForms 返回標準的 Python unicode 字符串,所以咱們 必須使用 |safe 過濾器告訴 Jinja2 這些數據已經通過 HTML 轉義了。

如下是使用了上面的 _formhelpers.html 的 register.html 模板:

{% from "_formhelpers.html" import render_field %} <form method=post action="/register"> <dl> {{ render_field(form.username) }} {{ render_field(form.email) }} {{ render_field(form.password) }} {{ render_field(form.confirm) }} {{ render_field(form.accept_tos) }} </dl> <p><input type=submit value=Register> </form> 

更多關於 WTForms 的信息請移步 WTForms 官方網站 。

模板繼承

Jinja 最有力的部分就是模板繼承。模板繼承容許你建立一個基礎「骨架」模板。這個模板 中包含站點的經常使用元素,定義能夠被子模板繼承的  。

聽起來很複雜其實作起來簡單,看看下面的例子就容易理解了。

基礎模板

這個模板的名稱是 layout.html ,它定義了一個簡單的 HTML 骨架,用於顯示一個 簡單的兩欄頁面。「子」模板的任務是用內容填充空的塊:

<!doctype html>
<html> <head> {% block head %} <link rel="stylesheet" href="{{ url_for('static', filename='style.css') }}"> <title>{% block title %}{% endblock %} - My Webpage</title> {% endblock %} </head> <body> <div id="content">{% block content %}{% endblock %}</div> <div id="footer"> {% block footer %} &copy; Copyright 2010 by <a href="http://domain.invalid/">you</a>. {% endblock %} </div> </body> </html> 

在這個例子中, {% block %} 標記定義了四個能夠被子模板填充的塊。 block 標記告訴模板引擎這是一個能夠被子模板重載的部分。

子模板

子模板示例:

{% extends "layout.html" %} {% block title %}Index{% endblock %} {% block head %} {{ super() }} <style type="text/css"> .important { color: #336699; } </style> {% endblock %} {% block content %} <h1>Index</h1> <p class="important"> Welcome on my awesome homepage. {% endblock %} 

這裏 {% extends %} 標記是關鍵,它告訴模板引擎這個模板「擴展」了另外一個模板, 當模板系統評估這個模板時會先找到父模板。這個擴展標記必須是模板中的第一個標記。 若是要使用父模板中的塊內容,請使用 {{ super() }} 。

消息閃現

一個好的應用和用戶界面都須要良好的反饋。若是用戶得不到足夠的反饋,那麼應用最終 會被用戶唾棄。 Flask 的閃現系統提供了一個良好的反饋方式。閃現系統的基本工做方式 是:在且只在下一個請求中訪問上一個請求結束時記錄的消息。通常咱們結合佈局模板來 使用閃現系統。

簡單的例子

如下是一個完整的示例:

from flask import Flask, flash, redirect, render_template, \ request, url_for app = Flask(__name__) app.secret_key = 'some_secret' @app.route('/') def index(): return render_template('index.html') @app.route('/login', methods=['GET', 'POST']) def login(): error = None if request.method == 'POST': if request.form['username'] != 'admin' or \ request.form['password'] != 'secret': error = 'Invalid credentials' else: flash('You were successfully logged in') return redirect(url_for('index')) return render_template('login.html', error=error) if __name__ == "__main__": app.run() 

如下是實現閃現的 layout.html 模板:

<!doctype html>
<title>My Application</title> {% with messages = get_flashed_messages() %} {% if messages %} <ul class=flashes> {% for message in messages %} <li>{{ message }}</li> {% endfor %} </ul> {% endif %} {% endwith %} {% block body %}{% endblock %} 

如下是 index.html 模板:

{% extends "layout.html" %} {% block body %} <h1>Overview</h1> <p>Do you want to <a href="{{ url_for('login') }}">log in?</a> {% endblock %} 

login 模板:

{% extends "layout.html" %} {% block body %} <h1>Login</h1> {% if error %} <p class=error><strong>Error:</strong> {{ error }} {% endif %} <form action="" method=post> <dl> <dt>Username: <dd><input type=text name=username value="{{ request.form.username }}"> <dt>Password: <dd><input type=password name=password> </dl> <p><input type=submit value=Login> </form> {% endblock %} 
閃現消息的類別

New in version 0.3.

閃現消息還能夠指定類別,若是沒有指定,那麼缺省的類別爲 'message' 。不一樣的 類別能夠給用戶提供更好的反饋。例如錯誤消息能夠使用紅色背景。

使用 flash() 函數能夠指定消息的類別:

flash(u'Invalid password provided', 'error') 

模板中的 get_flashed_messages() 函數也應當返回類別,顯示消息的循環 也要略做改變:

{% with messages = get_flashed_messages(with_categories=true) %} {% if messages %} <ul class=flashes> {% for category, message in messages %} <li class="{{ category }}">{{ message }}</li> {% endfor %} </ul> {% endif %} {% endwith %} 

上例展現如何根據類別渲染消息,還能夠給消息加上前綴,如 <strong>Error:</strong> 。

過濾閃現消息

New in version 0.9.

你能夠視狀況經過傳遞一個類別列表來過濾 get_flashed_messages() 的 結果。這個功能有助於在不一樣位置顯示不一樣類別的消息。

{% with errors = get_flashed_messages(category_filter=["error"]) %} {% if errors %} <div class="alert-message block-message error"> <a class="close" href="#"</a> <ul> {%- for msg in errors %} <li>{{ msg }}</li> {% endfor -%} </ul> </div> {% endif %} {% endwith %} 

經過 jQuery 使用 AJAX

jQuery 是一個小型的 JavaScript 庫,一般用於簡化 DOM 和 JavaScript 的使用。它 是一個很是好的工具,能夠經過在服務端和客戶端交換 JSON 來使網絡應用更具備動態性。

JSON 是一種很是輕巧的傳輸格式,很是相似於 Python 原語(數字、字符串、字典和列表 )。 JSON 被普遍支持,易於解析。 JSON 在幾年以前開始流行,在網絡應用中迅速取代 了 XML 。

載入 jQuery

爲了使用 jQuery ,你必須先把它下載下來,放在應用的靜態目錄中,並確保它被載入。 理想狀況下你有一個用於全部頁面的佈局模板。在這個模板的 <body> 的底部添加一個 script 語句來載入 jQuery :

<script type=text/javascript src="{{  url_for('static', filename='jquery.js') }}"></script> 

另外一個方法是使用 Google 的 AJAX 庫 API 來載入 jQuery:

<script src="//ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script> <script>window.jQuery || document.write('<script src="{{  url_for('static', filename='jquery.js') }}">\x3C/script>')</script> 

在這種方式中,應用會先嚐試從 Google 下載 jQuery ,若是失敗則會調用靜態目錄中的 備用 jQuery 。這樣作的好處是若是用戶已經去過使用與 Google 相同版本的 jQuery 的 網站後,訪問你的網站時,頁面可能會更快地載入,由於瀏覽器已經緩存了 jQuery 。

個人網站在哪裏?

個人網站在哪裏?若是你的應用還在開發中,那麼答案很簡單:它在本機的某個端口上, 且在服務器的根路徑下。可是若是之後要把應用移到其餘位置(例如http://example.com/myapp )上呢?在服務端,這個問題不成爲問題,能夠使用url_for() 函數來獲得答案。可是若是咱們使用 jQuery ,那麼就不能硬碼應用的路徑,只能使用動態路徑。怎麼辦?

一個簡單的方法是在頁面中添加一個 script 標記,設置一個全局變量來表示應用的根 路徑。示例:

<script type=text/javascript> $SCRIPT_ROOT = {{ request.script_root|tojson|safe }}; </script> 

在 Flask 0.10 版本之前版本中,使用 |safe 是有必要的,是爲了使 Jinja 不要 轉義 JSON 編碼的字符串。一般這樣作不是必須的,可是在 script 內部咱們須要這麼 作。

進一步說明

在 HTML 中, script 標記是用於聲明 CDATA 的,也就是說聲明的內容不會被 解析。<script> 與 </script> 之間的內容都會被做爲腳本處理。這也意味着 在 script 標記之間不會存在任何 </ 。在這裏 |tojson 會正確處理問題, 併爲你轉義斜槓( {{ "</script>"|tojson|safe }} 會被渲染爲 "<\/script>" )。

在 Flask 0.10 版本中更進了一步,把全部 HTML 標記都用 unicode 轉義了,這樣使 Flask 自動把 HTML 轉換爲安全標記。

JSON 視圖函數

如今讓咱們來建立一個服務端函數,這個函數接收兩個 URL 參數(兩個須要相加的數字 ),而後嚮應用返回一個 JSON 對象。下面這個例子是很是不實用的,由於通常會在 客戶端完成相似工做,但這個例子能夠簡單明瞭地展現如何使用 jQuery 和 Flask:

from flask import Flask, jsonify, render_template, request app = Flask(__name__) @app.route('/_add_numbers') def add_numbers(): a = request.args.get('a', 0, type=int) b = request.args.get('b', 0, type=int) return jsonify(result=a + b) @app.route('/') def index(): return render_template('index.html') 

正如你所見,我還添加了一個 index 方法來渲染模板。這個模板會按前文所述載入 jQuery 。模板中有一個用於兩個數字相加的表單和一個觸發服務端函數的連接。

注意,這裏咱們使用了 get() 方法。它 不會調用失敗。若是字典的鍵不存在,就會返回一個缺省值(這裏是 0 )。更進一步 它還會把值轉換爲指定的格式(這裏是 int )。在腳本( API 、 JavaScript 等) 觸發的代碼中使用它特別方便,由於在這種狀況下不須要特殊的錯誤報告。

HTML

你的 index.html 模板要麼繼承一個已經載入 jQuery 和設置好 $SCRIPT_ROOT 變量的layout.html 模板,要麼在模板開頭就作好那兩件事。下面就是應用的 HTML 示例 (index.html )。注意,咱們把腳本直接放入了 HTML 中。一般更好的方式是放在 獨立的腳本文件中:

<script type=text/javascript> $(function() { $('a#calculate').bind('click', function() { $.getJSON($SCRIPT_ROOT + '/_add_numbers', { a: $('input[name="a"]').val(), b: $('input[name="b"]').val() }, function(data) { $("#result").text(data.result); }); return false; }); }); </script> <h1>jQuery Example</h1> <p><input type=text size=5 name=a> + <input type=text size=5 name=b> = <span id=result>?</span> <p><a href=# id=calculate>calculate server side</a> 

這裏不講述 jQuery 運行詳細狀況,僅對上例做一個簡單說明:

  1. $(function() ... }) 定義瀏覽器在頁面的基本部分載入完成後當即執行的. 代碼。
  2. $('selector') 選擇一個元素供你操做。
  3. element.bind('event', func) 定義一個用戶點擊元素時運行的函數。若是函數 返回 false ,那麼缺省行爲就不會起做用(本例爲轉向 # URL )。
  4. $.getJSON(url, data, func) 向 url 發送一個 GET 請求,並把 data 對象的內容做爲查詢參數。一旦有數據返回,它將調用指定的函數,並把返回值做爲 函數的參數。注意,咱們能夠在這裏使用先前定義的 $SCRIPT_ROOT 變量。

若是你沒有一個完整的概念,請從 github 下載 示例源代碼 。

自定義出錯頁面

Flask 有一個方便的 abort() 函數,它能夠經過一個 HTTP 出錯代碼退出 一個請求。它還提供一個包含基本說明的出錯頁面,頁面顯示黑白的文本,很樸素。

用戶能夠根據錯誤代碼或多或少知道發生了什麼錯誤。

常見出錯代碼

如下出錯代碼是用戶常見的,即便應用正常也會出現這些出錯代碼:

404 Not Found
這是一個古老的「朋友,你使用了一個錯誤的 URL 」信息。這個信息出現得如此 頻繁,以致於連剛上網的新手都知道 404 表明:該死的,我要看的東西不見了。 一個好的作法是確保 404 頁面上有一些真正有用的東西,至少要有一個返回首頁 的連接。
403 Forbidden
若是你的網站上有某種權限控制,那麼當用戶訪問未獲受權內容時應當發送 403 代碼。所以請確保當用戶嘗試訪問未獲受權內容時獲得正確的反饋。
410 Gone
你知道 「404 Not Found」 有一個名叫 「410 Gone」 的兄弟嗎?不多有人使用這個 代碼。若是資源之前曾經存在過,可是如今已經被刪除了,那麼就應該使用 410 代碼,而不是 404 。若是你不是在數據庫中把文檔永久地刪除,而只是給文檔打 了一個刪除標記,那麼請爲用戶考慮,應當使用 410 代碼,並顯示信息告知用戶 要找的東西已經刪除。
500 Internal Server Error
這個代碼一般表示程序出錯或服務器過載。強烈建議把這個頁面弄得友好一點, 由於你的應用  早晚 會出現故障的(參見  掌握應用錯誤 )。
出錯處理器

一個出錯處理器是一個函數,就像一個視圖函數同樣。與視圖函數不一樣之處在於出錯處理器 在出現錯誤時被調用,且傳遞錯誤。錯誤大多數是一個 HTTPException ,可是有一個例外:當出現內部服務器錯誤 時會把異常實例傳遞給出錯處理器。

出錯處理器使用 errorhandler() 裝飾器註冊,註冊時應提供異常的 出代碼。請記住, Flask 不會 爲你設置出錯代碼,所以請確保在返回響應時,同時提供 HTTP 狀態代碼。

如下是一個處理 「404 Page Not Found」 異常的示例:

from flask import render_template @app.errorhandler(404) def page_not_found(e): return render_template('404.html'), 404 

示例模板:

{% extends "layout.html" %} {% block title %}Page Not Found{% endblock %} {% block body %} <h1>Page Not Found</h1> <p>What you were looking for is just not there. <p><a href="{{ url_for('index') }}">go somewhere nice</a> {% endblock %} 

惰性載入視圖

Flask 一般使用裝飾器。裝飾器簡單易用,只要把 URL 放在相應的函數的前面就能夠了。 可是這種方式有一個缺點:使用裝飾器的代碼必須預先導入,不然 Flask 就沒法真正找到 你的函數。

當你必須快速導入應用時,這就會成爲一個問題。在 Google App Engine 或其餘系統中, 必須快速導入應用。所以,若是你的應用存在這個問題,那麼必須使用集中 URL 映射。

add_url_rule() 函數用於集中 URL 映射,與使用裝飾器不一樣的是你 須要一個設置應用全部 URL 的專門文件。

轉換爲集中 URL 映射

假設有以下應用:

from flask import Flask app = Flask(__name__) @app.route('/') def index(): pass @app.route('/user/<username>') def user(username): pass 

爲了集中映射,咱們建立一個不使用裝飾器的文件( views.py ):

def index(): pass def user(username): pass 

在另外一個文件中集中映射函數與 URL:

from flask import Flask from yourapplication import views app = Flask(__name__) app.add_url_rule('/', view_func=views.index) app.add_url_rule('/user/<username>', view_func=views.user) 
延遲載入

至此,咱們只是把視圖與路由分離,可是模塊仍是預先載入了。理想的方式是按需載入 視圖。下面咱們使用一個相似函數的輔助類來實現按需載入:

from werkzeug import import_string, cached_property class LazyView(object): def __init__(self, import_name): self.__module__, self.__name__ = import_name.rsplit('.', 1) self.import_name = import_name @cached_property def view(self): return import_string(self.import_name) def __call__(self, *args, **kwargs): return self.view(*args, **kwargs) 

上例中最重要的是正確設置 __module__ 和 __name__ ,它被用於在不提供 URL 規則 的狀況下正確命名 URL 規則。

而後能夠這樣集中定義 URL 規則:

from flask import Flask from yourapplication.helpers import LazyView app = Flask(__name__) app.add_url_rule('/', view_func=LazyView('yourapplication.views.index')) app.add_url_rule('/user/<username>', view_func=LazyView('yourapplication.views.user')) 

還能夠進一步優化代碼:寫一個函數調用 add_url_rule() ,加上 應用前綴和點符號:

def url(url_rule, import_name, **options): view = LazyView('yourapplication.' + import_name) app.add_url_rule(url_rule, view_func=view, **options) url('/', 'views.index') url('/user/<username>', 'views.user') 

有一件事情要牢記:請求前和請求後處理器必須在第一個請求前導入。

其他的裝飾器能夠一樣用上述方法改寫。

在 Flask 中使用 MongoKit

如今使用文檔型數據庫來取代關係型數據庫已愈來愈常見。本方案展現如何使用 MongoKit ,它是一個用於操做 MongoDB 的文檔映射庫。

本方案須要一個運行中的 MongoDB 服務器和已安裝好的 MongoKit 庫。

使用 MongoKit 有兩種經常使用的方法,下面逐一說明:

聲明

聲明是 MongoKit 的缺省行爲。這個思路來自於 Django 或 SQLAlchemy 的聲明。

下面是一個示例 app.py 模塊:

from flask import Flask from mongokit import Connection, Document # configuration MONGODB_HOST = 'localhost' MONGODB_PORT = 27017 # create the little application object app = Flask(__name__) app.config.from_object(__name__) # connect to the database connection = Connection(app.config['MONGODB_HOST'], app.config['MONGODB_PORT']) 

若是要定義模型,那麼只要繼承 MongoKit 的 Document 類就好了。若是你已經讀過 SQLAlchemy 方案,那麼能夠會奇怪這裏爲何沒有使用會話,甚至沒有定義一個init_db 函數。一方面是由於 MongoKit 沒有相似會話在東西。有時候這樣會多寫一點 代碼,但會使它的速度更快。另外一方面是由於 MongoDB 是無模式的。這就意味着能夠在 插入數據的時候修改數據結構。 MongoKit 也是無模式的,但會執行一些驗證,以確保 數據的完整性。

如下是一個示例文檔(把示例內容也放入 app.py ):

def max_length(length): def validate(value): if len(value) <= length: return True raise Exception('%s must be at most %s characters long' % length) return validate class User(Document): structure = { 'name': unicode, 'email': unicode, } validators = { 'name': max_length(50), 'email': max_length(120) } use_dot_notation = True def __repr__(self): return '<User %r>' % (self.name) # 在當前鏈接中註冊用戶文檔 connection.register([User]) 

上例展現如何定義模式(命名結構)和字符串最大長度驗證器。上例中還使用了一個 MongoKit 中特殊的 use_dot_notation 功能。缺省狀況下, MongoKit 的運做方式和 Python 的字典相似。可是若是 use_dot_notation 設置爲 True ,那麼就可幾乎像 其餘 ORM 同樣使用點符號來分隔屬性。

能夠像下面這樣把條目插入數據庫中:

>>> from yourapplication.database import connection >>> from yourapplication.models import User >>> collection = connection['test'].users >>> user = collection.User() >>> user['name'] = u'admin' >>> user['email'] = u'admin@localhost' >>> user.save() 

注意, MongoKit 對於列類型的使用是比較嚴格的。對於 name 和 email 列,你都 不能使用 str 類型,應當使用 unicode 。

查詢很是簡單:

>>> list(collection.User.find()) [<User u'admin'>] >>> collection.User.find_one({'name': u'admin'}) <User u'admin'> 
PyMongo 兼容層

若是你只須要使用 PyMongo ,也能夠使用 MongoKit 。在這種方式下能夠得到最佳的 性能。注意,如下示例中,沒有 MongoKit 與 Flask 整合的內容,整合的方式參見上文:

from MongoKit import Connection connection = Connection() 

使用 insert 方法能夠插入數據。但首先必須先獲得一個鏈接。這個鏈接相似於 SQL 界 的表。

>>> collection = connection['test'].users >>> user = {'name': u'admin', 'email': u'admin@localhost'} >>> collection.insert(user) 

MongoKit 會自動提交。

直接使用集合查詢數據庫:

>>> list(collection.find()) [{u'_id': ObjectId('4c271729e13823182f000000'), u'name': u'admin', u'email': u'admin@localhost'}] >>> collection.find_one({'name': u'admin'}) {u'_id': ObjectId('4c271729e13823182f000000'), u'name': u'admin', u'email': u'admin@localhost'} 

查詢結果爲類字典對象:

>>> r = collection.find_one({'name': u'admin'}) >>> r['email'] u'admin@localhost' 

關於 MongoKit 的更多信息,請移步其 官方網站 。

添加一個頁面圖標

一個「頁面圖標」是瀏覽器在標籤或書籤中使用的圖標,它能夠給你的網站加上一個惟一 的標示,方便區別於其餘網站。

那麼如何給一個 Flask 應用添加一個頁面圖標呢?首先,顯而易見的,須要一個圖標。 圖標應當是 16 X 16 像素的 ICO 格式文件。這不是規定的,但倒是一個全部瀏覽器都 支持的事實上的標準。把 ICO 文件命名爲 favicon.ico 並放入靜態 文件目錄 中。

如今咱們要讓瀏覽器可以找到你的圖標,正確的作法是在你的 HTML 中添加一個連接。 示例:

<link rel="shortcut icon" href="{{ url_for('static', filename='favicon.ico') }}"> 

對於大多數瀏覽器來講,這樣就完成任務了,可是一些老古董不支持這個標準。老的標準 是把名爲「 favicon.ico 」的圖標放在服務器的根目錄下。若是你的應用不是掛接在域的 根目錄下,那麼你須要定義網頁服務器在根目錄下提供這個圖標,不然就機關用盡了。 若是你的應用位於根目錄下,那麼你能夠簡單地進行重定向:

app.add_url_rule('/favicon.ico', redirect_to=url_for('static', filename='favicon.ico')) 

若是想要保存額外的重定向請求,那麼還能夠使用 send_from_directory() 函數來寫一個視圖:

import os from flask import send_from_directory @app.route('/favicon.ico') def favicon(): return send_from_directory(os.path.join(app.root_path, 'static'), 'favicon.ico', mimetype='image/vnd.microsoft.icon') 

上例中的 MIME 類型能夠省略,瀏覽器會自動猜想類型。可是咱們在例子中明肯定義了, 省去了額外的猜想,反正這個類型是不變的。

上例會經過你的應用來提供圖標,若是可能的話,最好配置你的專用服務器來提供圖標, 配置方法參見網頁服務器的文檔。

另見

流內容

有時候你會須要把大量數據傳送到客戶端,不在內存中保存這些數據。當你想把運行中產生 的數據不通過文件系統,而是直接發送給客戶端時,應當怎麼作呢?

答案是使用生成器和直接響應。

基本用法

下面是一個在運行中產生大量 CSV 數據的基本視圖函數。其技巧是調用一個內聯函數生成 數據,把這個函數傳遞給一個響應對象:

from flask import Response @app.route('/large.csv') def generate_large_csv(): def generate(): for row in iter_all_rows(): yield ','.join(row) + '\n' return Response(generate(), mimetype='text/csv') 

每一個 yield 表達式被直接傳送給瀏覽器。注意,有一些 WSGI 中間件可能會打斷流 內容,所以在使用分析器或者其餘工具的調試環境中要當心一些。

模板中的流內容

Jinja2 模板引擎也支持分片渲染模板。這個功能不是直接被 Flask 支持的,由於它太 特殊了,可是你能夠方便地自已來作:

from flask import Response def stream_template(template_name, **context): app.update_template_context(context) t = app.jinja_env.get_template(template_name) rv = t.stream(context) rv.enable_buffering(5) return rv @app.route('/my-large-page.html') def render_large_template(): rows = iter_all_rows() return Response(stream_template('the_template.html', rows=rows)) 

上例的技巧是從 Jinja2 環境中得到應用的模板對象,並調用 stream() 來代替render() ,返回 一個流對象來代替一個字符串。因爲咱們繞過了 Flask 的模板渲染函數使用了模板對象 自己,所以咱們須要調用 update_template_context() ,以確保 更新被渲染的內容。這樣,模板遍歷流內容。因爲每次產生內容後,服務器都會把內容 發送給客戶端,所以可能須要緩存來保存內容。咱們使用了rv.enable_buffering(size) 來進行緩存。 5 是一個比較明智的缺省值。

環境中的流內容

New in version 0.9.

注意,當你生成流內容時,請求環境已經在函數執行時消失了。 Flask 0.9 爲你提供了 一點幫助,讓你能夠在生成器運行期間保持請求環境:

from flask import stream_with_context, request, Response @app.route('/stream') def streamed_response(): def generate(): yield 'Hello ' yield request.args['name'] yield '!' return Response(stream_with_context(generate())) 

若是沒有使用 stream_with_context() 函數,那麼就會引起 RuntimeError 錯誤。

延遲的請求回調

Flask 的設計思路之一是:響應對象建立後被傳遞給一串回調函數,這些回調函數能夠修改 或替換響應對象。當請求處理開始的時候,響應對象尚未被建立。響應對象是由一個視圖 函數或者系統中的其餘組件按需建立的。

可是當響應對象尚未建立時,咱們如何修改響應對象呢?好比在一個請求前函數中,咱們 須要根據響應對象設置一個 cookie 。

一般咱們選擇避開這種情形。例如能夠嘗試把應用邏輯移動到請求後函數中。可是,有時候 這個方法讓人不爽,或者讓代碼變得很醜陋。

變通的方法是把一堆回調函數貼到 g 對象上,而且在請求結束時調用這些 回調函數。這樣你就能夠在應用的任意地方延遲迴調函數的執行。

裝飾器

下面的裝飾器是一個關鍵,它在 g 對象上註冊一個函數列表:

from flask import g def after_this_request(f): if not hasattr(g, 'after_request_callbacks'): g.after_request_callbacks = [] g.after_request_callbacks.append(f) return f 
調用延遲的回調函數

至此,經過使用 after_this_request 裝飾器,使得函數在請求結束時能夠被調用。如今 咱們來實現這個調用過程。咱們把這些函數註冊爲 after_request() 回調函數:

@app.after_request
def call_after_request_callbacks(response): for callback in getattr(g, 'after_request_callbacks', ()): callback(response) return response 
一個實例

如今咱們能夠方便地隨時隨地爲特定請求註冊一個函數,讓這個函數在請求結束時被調用。 例如,你能夠在請求前函數中把用戶的當前語言記錄到 cookie 中:

from flask import request @app.before_request def detect_user_language(): language = request.cookies.get('user_lang') if language is None: language = guess_language_from_request() @after_this_request def remember_language(response): response.set_cookie('user_lang', language) g.language = language 

添加 HTTP 方法重載

一些 HTTP 代理不支持全部 HTTP 方法或者不支持一些較新的 HTTP 方法(例如 PACTH )。在這種狀況下,能夠經過使用徹底相反的協議,用一種 HTTP 方法來「代理」另外一種 HTTP 方法。

實現的思路是讓客戶端發送一個 HTTP POST 請求,並設置 X-HTTP-Method-Override頭部爲須要的 HTTP 方法(例如 PATCH )。

經過 HTTP 中間件能夠方便的實現:

class HTTPMethodOverrideMiddleware(object): allowed_methods = frozenset([ 'GET', 'HEAD', 'POST', 'DELETE', 'PUT', 'PATCH', 'OPTIONS' ]) bodyless_methods = frozenset(['GET', 'HEAD', 'OPTIONS', 'DELETE']) def __init__(self, app): self.app = app def __call__(self, environ, start_response): method = environ.get('HTTP_X_HTTP_METHOD_OVERRIDE', '').upper() if method in self.allowed_methods: method = method.encode('ascii', 'replace') environ['REQUEST_METHOD'] = method if method in self.bodyless_methods: environ['CONTENT_LENGTH'] = '0' return self.app(environ, start_response) 

經過如下代碼就能夠與 Flask 一同工做了:

from flask import Flask app = Flask(__name__) app.wsgi_app = HTTPMethodOverrideMiddleware(app.wsgi_app) 

請求內容校驗

請求數據會由不一樣的代碼來處理或者預處理。例如 JSON 數據和表單數據都來源於已經 讀取並處理的請求對象,可是它們的處理代碼是不一樣的。這樣,當須要校驗進來的請求 數據時就會遇到麻煩。所以,有時候就有必要使用一些 API 。

幸運的是能夠經過包裝輸入流來方便地改變這種狀況。

下面的例子演示在 WSGI 環境下讀取和儲存輸入數據,獲得數據的 SHA1 校驗:

import hashlib class ChecksumCalcStream(object): def __init__(self, stream): self._stream = stream self._hash = hashlib.sha1() def read(self, bytes): rv = self._stream.read(bytes) self._hash.update(rv) return rv def readline(self, size_hint): rv = self._stream.readline(size_hint) self._hash.update(rv) return rv def generate_checksum(request): env = request.environ stream = ChecksumCalcStream(env['wsgi.input']) env['wsgi.input'] = stream return stream._hash 

要使用上面的類,你只要在請求開始消耗數據以前鉤接要計算的流就能夠了。(按:當心 操做 request.form 或相似東西。例如 before_request_handlers 就應當當心不 要操做。)

用法示例:

@app.route('/special-api', methods=['POST']) def special_api(): hash = generate_checksum(request) # Accessing this parses the input stream files = request.files # At this point the hash is fully constructed. checksum = hash.hexdigest() return 'Hash was: %s' % checksum 

基於 Celery 的後臺任務

Celery 是一個 Python 編寫的是一個異步任務隊列/基於分佈式消息傳遞的做業隊列。 之前它有一個 Flask 的集成,可是從版本 3 開始,它進行了一些內部的重構,已經 不須要這個集成了。本文主要說明如何在 Flask 中正確使用 Celery 。本文假設你 已經閱讀過了其官方文檔中的 Celery 入門

安裝 Celery

Celery 在 Python 包索引( PyPI )上榜上有名,所以能夠使用 pip 或 easy_install 之類標準的 Python 工具來安裝:

$ pip install celery
配置 Celery

你首先須要有一個 Celery 實例,這個實例稱爲 celery 應用。其地位就至關於 Flask 中Flask 同樣。這個實例被用做全部 Celery 相關事務的入口,例如建立 任務、管理工人等等。所以它必須能夠被其餘模塊導入。

例如,你能夠把它放在一個 tasks 模塊中。這樣不須要從新配置,你就能夠使用 tasks 的子類,增長 Flask 應用環境的支持,並鉤接 Flask 的配置。

只要以下這樣就能夠在 Falsk 中使用 Celery 了:

from celery import Celery def make_celery(app): celery = Celery(app.import_name, broker=app.config['CELERY_BROKER_URL']) celery.conf.update(app.config) TaskBase = celery.Task class ContextTask(TaskBase): abstract = True def __call__(self, *args, **kwargs): with app.app_context(): return TaskBase.__call__(self, *args, **kwargs) celery.Task = ContextTask return celery 

這個函數建立了一個新的 Celery 對象,使用了應用配置中的 broker ,並從 Flask 配置 中升級了 Celery 的其他配置。而後建立了一個任務子類,在一個應用環境中包裝了任務 執行。

最小的例子

基於上文,如下是一個在 Flask 中使用 Celery 的最小例子:

from flask import Flask app = Flask(__name__) app.config.update( CELERY_BROKER_URL='redis://localhost:6379', CELERY_RESULT_BACKEND='redis://localhost:6379' ) celery = make_celery(app) @celery.task() def add_together(a, b): return a + b 

這個任務如今能夠在後臺調用了:

>>> result = add_together.delay(23, 42) >>> result.wait() 65 
運行 Celery 工人

至此,若是你已經按上文一步一步執行,你會失望地發現你的 .wait() 不會真正 返回。這是由於你尚未運行 celery 。你能夠這樣以工人方式運行 celery:

$ celery -A your_application worker

把 your_application 字符串替換爲你建立 celery 對像的應用包或模塊。

部署方式

Flask 應用能夠採用多種方式部署。在開發時,你能夠使用內置的服務器,可是在生產環境 下你就應當選擇功能完整的服務器。下面爲你提供幾個可用的選擇。

除了下面提到的服務器以外,若是你使用了其餘的 WSGI 服務器,那麼請閱讀其文檔中與 使用 WSGI 應用相關的部分。由於 Flask 應用對象的實質就是一個 WSGI 應用。

若是須要快速體驗,請參閱《快速上手》中的 部署到一個網絡服務器 。

mod_wsgi (Apache)

若是你正在使用 Apache 網絡服務器,那麼建議使用 mod_wsgi 。

當心

請務必把 app.run() 放在 if __name__ == '__main__': 內部或者放在單獨 的文件中,這樣能夠保證它不會被調用。由於,每調用一次就會開啓一個本地 WSGI 服務器。當咱們使用 mod_wsgi 部署應用時,不須要使用本地服務器。

安裝 mod_wsgi

能夠使用包管理器或編譯的方式安裝 mod_wsgi 。在 UNIX 系統中如何使用源代碼安裝 請閱讀 mod_wsgi 安裝介紹 。

若是你使用的是 Ubuntu/Debian ,那麼能夠使用以下命令安裝:

# apt-get install libapache2-mod-wsgi

在 FreeBSD 系統中,能夠經過編譯 www/mod_wsgi port 或使用 pkg_add 來安裝mod_wsgi :

# pkg_add -r mod_wsgi

若是你使用 pkgsrc ,那麼能夠經過編譯 www/ap2-wsgi 包來安裝 mod_wsgi 。

若是你遇到子進程段錯誤的話,不要理它,重啓服務器就能夠了。

建立一個 .wsgi 文件

爲了運行應用,你須要一個 yourapplication.wsgi 文件。這個文件包含 mod_wsgi 開始時須要運行的代碼,經過代碼能夠得到應用對象。文件中的 application 對象就 是之後要使用的應用。

對於大多數應用來講,文件包含如下內容就能夠了:

from yourapplication import app as application 

若是你的應用沒有建立函數,只是一個獨立的實例,那麼能夠直接把實例導入爲application 。

把文件放在一個之後能夠找獲得的地方(例如 /var/www/yourapplication ),並確保yourapplication 和全部須要使用的庫都位於 pythonpath 中。若是你不想在整個系統 中安裝,建議使用 virtual python 實例。請記住,最好把應用安裝到虛擬環境中。 有一個可選項是在 .wsgi 文件中,在導入前加入路徑:

import sys sys.path.insert(0, '/path/to/the/application') 
配置 Apache

最後一件事是爲你的應用建立一個 Apache 配置文件。基於安全緣由,在下例中咱們告訴mod_wsgi 使用另一個用戶運行應用:

<VirtualHost *> ServerName example.com WSGIDaemonProcess yourapplication user=user1 group=group1 threads=5 WSGIScriptAlias / /var/www/yourapplication/yourapplication.wsgi <Directory /var/www/yourapplication> WSGIProcessGroup yourapplication WSGIApplicationGroup %{GLOBAL} Order deny,allow Allow from all </Directory> </VirtualHost> 

注意: WSGIDaemonProcess 在 Windows 中不會被執行, 使用上面的配置 Apache 會拒絕 運行。在 Windows 系統下,請使用下面內容:

<VirtualHost *> ServerName example.com WSGIScriptAlias / C:\yourdir\yourapp.wsgi <Directory C:\yourdir> Order deny,allow Allow from all </Directory> </VirtualHost> 

更多信息參見 mod_wsgi 維基 。

故障排除

若是你的應用沒法運行,請按如下指導排除故障:

問題: 應用沒法運行,出錯記錄顯示 SystemExit ignored
應用文件中有  app.run() 調用,但沒有放在  if __name__ == '__main__': 塊內。要麼把這個調用放入塊內,要麼把它放在一個單獨的  run.py 文件中。
問題: 權限錯誤
有能夠是由於使用了錯誤的用戶運行應用。請檢查用戶及其所在的組 ( WSGIDaemonProcess 的  user 和  group 參數)是否有權限訪問應用 文件夾。
問題: 打印時應用歇菜

請記住 mod_wsgi 不容許使用 sys.stdout 和 sys.stderr 。把WSGIRestrictStdout 設置爲 off 能夠去掉這個保護:

WSGIRestrictStdout Off 

或者你能夠在 .wsgi 文件中把標準輸出替換爲其餘的流:

import sys sys.stdout = sys.stderr 
問題: 訪問資源時遇到 IO 錯誤

你的應用多是一個獨立的 .py 文件,且你把它符號鏈接到了 site-packages 文件夾。這樣是不對的,你應當要麼把文件夾放到 pythonpath 中,要麼把你的應用 轉換爲一個包。

產生這種錯誤的緣由是對於非安裝包來講,模塊的文件名用於定位資源,若是使用 符號鏈接的話就會定位到錯誤的文件名。

支持自動重載

爲了輔助部署工具,你能夠激活自動重載。這樣,一旦 .wsgi 文件有所變更,mod_wsgi 就會自動從新轉入全部守護進程。

在 Directory 一節中加入如下指令就能夠實現自動重載:

WSGIScriptReloading On 
使用虛擬環境

使用虛擬環境的優勢是沒必要全局安裝應用所須要的依賴,這樣咱們就能夠更好地按照本身 的須要進行控制。若是要在虛擬環境下使用 mod_wsgi ,那麼咱們要對 .wsgi 略做 改變。

在你的 .wsgi 文件頂部加入下列內容:

activate_this = '/path/to/env/bin/activate_this.py' execfile(activate_this, dict(__file__=activate_this)) 

以上代碼會根據虛擬環境的設置載入相關路徑。請記住路徑必須是絕對路徑。

獨立 WSGI 容器

有一些用 Python 寫的流行服務器能夠容納 WSGI 應用,提供 HTTP 服務。這些服務器是 獨立運行的,你能夠把代理從你的網絡服務器指向它們。若是遇到問題,請閱讀 代理設置 一節。

Gunicorn

Gunicorn ‘Green Unicorn’ 是一個 UNIX 下的 WSGI HTTP 服務器,它是一個 移植自 Ruby 的 Unicorn 項目的 pre-fork worker 模型。它既支持 eventlet , 也支持 greenlet 。在 Gunicorn 上運行 Flask 應用很是簡單:

gunicorn myproject:app

Gunicorn 提供許多命令行參數,能夠使用 gunicorn -h 來得到幫助。下面的例子 使用 4 worker 進程( -w 4 )來運行 Flask 應用,綁定到 localhost 的 4000 端口( -b127.0.0.1:4000 ):

gunicorn -w 4 -b 127.0.0.1:4000 myproject:app
Tornado

Tornado 是構建 FriendFeed 的服務器和工具的開源版本,具備良好的伸縮性,非 阻塞性。得益於其非阻塞的方式和對 epoll 的運用,它能夠同步處理數以千計的獨立 鏈接,所以 Tornado 是實時 Web 服務的一個理想框架。用它來服務 Flask 是小事一樁:

from tornado.wsgi import WSGIContainer from tornado.httpserver import HTTPServer from tornado.ioloop import IOLoop from yourapplication import app http_server = HTTPServer(WSGIContainer(app)) http_server.listen(5000) IOLoop.instance().start() 
Gevent

Gevent 是一個 Python 併發網絡庫,它使用了基於 libevent 事件循環的 greenlet 來提供一個高級同步 API:

from gevent.wsgi import WSGIServer from yourapplication import app http_server = WSGIServer(('', 5000), app) http_server.serve_forever() 
Twisted Web

Twisted Web 是一個 Twisted 自帶的網絡服務器,是一個成熟的、異步的、事件 驅動的網絡庫。 Twisted Web 帶有一個標準的 WSGI 容器,該容器能夠使用 twistd 工具運行命令行來控制:

twistd web --wsgi myproject.app

這個命令會運行一個名爲 app 的 Flask 應用,其模塊名爲 myproject 。

與 twistd 工具同樣, Twisted Web 支持許多標記和選項。更多信息參見 twistd -h 和twistd web -h 。例以下面命令在前臺運行一個來自 myproject 的應用, 端口爲 8080:

twistd -n web --port 8080 --wsgi myproject.app
代理設置

若是你要在一個 HTTP 代理後面在上述服務器上運行應用,那麼必須重寫一些頭部才行。 一般在 WSGI 環境中常常會出現問題的有兩個變量: REMOTE_ADDR 和HTTP_HOST 。 你能夠經過設置你的 httpd 來傳遞這些頭部,或者在中間件中修正這些問題。 Werkzeug 帶有一個修復工具能夠用於經常使用的設置,可是你可能須要爲特定的設置編寫你 本身的 WSGI 中間件。

下面是一個簡單的 nginx 配置,代理目標是 localhost 8000 端口提供的服務,設置了 適當的頭部:

server { listen 80; server_name _; access_log /var/log/nginx/access.log; error_log /var/log/nginx/error.log; location / { proxy_pass http://127.0.0.1:8000/; proxy_redirect off; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; } } 

若是你的 httpd 沒法提供這些頭部,那麼最經常使用的設置是調用 X-Forwarded-Host 定義 的主機和 X-Forwarded-For 定義的遠程地址:

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

頭部可信問題

請注意,在非代理狀況下使用這個中間件是有安全問題的,由於它會盲目信任惡意 客戶端發來的頭部。

若是你要根據另外一個頭部來重寫一個頭部,那麼能夠像下例同樣使用修復工具:

class CustomProxyFix(object): def __init__(self, app): self.app = app def __call__(self, environ, start_response): host = environ.get('HTTP_X_FHOST', '') if host: environ['HTTP_HOST'] = host return self.app(environ, start_response) app.wsgi_app = CustomProxyFix(app.wsgi_app) 

uWSGI

uWSGI 也是部署 Flask 的途徑之一,相似的部署途徑還有 nginx 、 lighttpd 和 cherokee。其餘部署途徑的信息參見 FastCGI 和 獨立 WSGI 容器 。使用 uWSGI 協議來部署 WSGI 應用的先決條件是 須要一個 uWSGI 服務器。 uWSGI 既是一個協議也是一個服務器。若是做爲一個服務器, 它能夠服務於 uWSGI 、 FastCGI 和 HTTP 協議。

最流行的 uWSGI 服務器是 uwsgi ,本文將使用它來舉例,請先安裝它。

當心

請務必把 app.run() 放在 if __name__ == '__main__': 內部或者放在單獨 的文件中,這樣能夠保證它不會被調用。由於,每調用一次就會開啓一個本地 WSGI 服務器。當咱們使用 uWSGI 部署應用時,不須要使用本地服務器。

使用 uwsgi 啓動你的應用

uwsgi 是基於 python 模塊中的 WSGI 調用的。假設 Flask 應用名稱爲 myapp.py , 能夠使用如下命令:

$ uwsgi -s /tmp/uwsgi.sock --module myapp --callable app

或者這個命令也行:

$ uwsgi -s /tmp/uwsgi.sock -w myapp:app
配置 nginx

一個 nginx 的基本 uWSGI 配置以下:

location = /yourapplication { rewrite ^ /yourapplication/; }
location /yourapplication { try_files $uri @yourapplication; }
location @yourapplication {
  include uwsgi_params;
  uwsgi_param SCRIPT_NAME /yourapplication;
  uwsgi_modifier1 30;
  uwsgi_pass unix:/tmp/uwsgi.sock;
}

這個配置把應用綁定到 /yourapplication 。若是你想要在根 URL 下運行應用很是 簡單,由於你沒必要指出 WSGI PATH_INFO 或讓 uwsgi 修改器來使用它:

location / { try_files $uri @yourapplication; }
location @yourapplication {
    include uwsgi_params;
    uwsgi_pass unix:/tmp/uwsgi.sock;
}

FastCGI

FastCGI 是部署 Flask 的途徑之一,相似的部署途徑還有 nginx 、 lighttpd 和 cherokee 。其餘部署途徑的信息參見 uWSGI 和 獨立 WSGI 容器 。本文講述的是使用 FastCGI 部署,所以先決條件 是要有一個 FastCGI 服務器。 flup 最流行的 FastCGI 服務器之一,咱們將會在本文 中使用它。在閱讀下文以前先安裝好 flup 。

當心

請務必把 app.run() 放在 if __name__ == '__main__': 內部或者放在單獨 的文件中,這樣能夠保證它不會被調用。由於,每調用一次就會開啓一個本地 WSGI 服務器。當咱們使用 FastCGI 部署應用時,不須要使用本地服務器。

建立一個 .fcgi 文件

首先你必須建立 FastCGI 服務器配置文件,咱們把它命名爲 yourapplication.fcgi:

#!/usr/bin/python
from flup.server.fcgi import WSGIServer from yourapplication import app if __name__ == '__main__': WSGIServer(app).run() 

若是使用的是 Apache ,那麼使用了這個文件以後就能夠正常工做了。可是若是使用的是 nginx 或老版本的 lighttpd ,那麼須要顯式地把接口傳遞給 FastCGI 服務器,即把接口 的路徑傳遞給 WSGIServer:

WSGIServer(application, bindAddress='/path/to/fcgi.sock').run() 

這個路徑必須與服務器配置中定義的路徑一致。

把這個 yourapplication.fcgi 文件放在一個之後能夠找獲得的地方,最好是/var/www/yourapplication 或相似的地方。

爲了讓服務器能夠執行這個文件,請給文件加上執行位,確保這個文件能夠執行:

# chmod +x /var/www/yourapplication/yourapplication.fcgi
配置 Apache

上面的例子對於基本的 Apache 部署已經夠用了,可是你的 .fcgi 文件會暴露在應用的 URL 中,好比 example.com/yourapplication.fcgi/news/ 。有多種方法能夠避免出現這 中狀況。一個較好的方法是使用 ScriptAlias 配置指令:

<VirtualHost *>
    ServerName example.com
    ScriptAlias / /path/to/yourapplication.fcgi/
</VirtualHost>

若是你沒法設置 ScriptAlias ,好比你使用的是一個共享的網絡主機,那麼你能夠使用 WSGI 中間件把 yourapplication.fcgi 從 URL 中刪除。你能夠這樣設置 .htaccess:

<IfModule mod_fcgid.c>
   AddHandler fcgid-script .fcgi
   <Files ~ (\.fcgi)>
       SetHandler fcgid-script
       Options +FollowSymLinks +ExecCGI
   </Files>
</IfModule>

<IfModule mod_rewrite.c>
   Options +FollowSymlinks
   RewriteEngine On
   RewriteBase /
   RewriteCond %{REQUEST_FILENAME} !-f
   RewriteRule ^(.*)$ yourapplication.fcgi/$1 [QSA,L]
</IfModule>

設置 yourapplication.fcgi:

#!/usr/bin/python
#: optional path to your local python site-packages folder import sys sys.path.insert(0, '<your_local_path>/lib/python2.6/site-packages') from flup.server.fcgi import WSGIServer from yourapplication import app class ScriptNameStripper(object): def __init__(self, app): self.app = app def __call__(self, environ, start_response): environ['SCRIPT_NAME'] = '' return self.app(environ, start_response) app = ScriptNameStripper(app) if __name__ == '__main__': WSGIServer(app).run() 
配置 lighttpd

一個 lighttpd 的基本 FastCGI 配置以下:

fastcgi.server = ("/yourapplication.fcgi" =>
    ((
        "socket" => "/tmp/yourapplication-fcgi.sock",
        "bin-path" => "/var/www/yourapplication/yourapplication.fcgi",
        "check-local" => "disable",
        "max-procs" => 1
    ))
)

alias.url = (
    "/static/" => "/path/to/your/static"
)

url.rewrite-once = (
    "^(/static($|/.*))$" => "$1",
    "^(/.*)$" => "/yourapplication.fcgi$1"

請記住啓用 FastCGI 、 alias 和 rewrite 模塊。以上配置把應用綁定到 /yourapplication。若是你想要讓應用在根 URL 下運行,那麼必須使用 LighttpdCGIRootFix 中間件來解決一個 lighttpd 缺陷。

請確保只有應用在根 URL 下運行時才使用上述中間件。更多信息請閱讀 FastCGI 和 Python (注意,已經再也不須要把一個接口顯式傳遞給 run() 了)。

配置 nginx

在 nginx 上安裝 FastCGI 應用有一些特殊,由於缺省狀況下不傳遞 FastCGI 參數。

一個 nginx 的基本 FastCGI 配置以下:

location = /yourapplication { rewrite ^ /yourapplication/ last; }
location /yourapplication { try_files $uri @yourapplication; }
location @yourapplication {
    include fastcgi_params;
    fastcgi_split_path_info ^(/yourapplication)(.*)$;
    fastcgi_param PATH_INFO $fastcgi_path_info;
    fastcgi_param SCRIPT_NAME $fastcgi_script_name;
    fastcgi_pass unix:/tmp/yourapplication-fcgi.sock;
}

這個配置把應用綁定到 /yourapplication 。若是你想要在根 URL 下運行應用很是 簡單,由於你沒必要指出如何計算出 PATH_INFO 和 SCRIPT_NAME:

location / { try_files $uri @yourapplication; }
location @yourapplication {
    include fastcgi_params;
    fastcgi_param PATH_INFO $fastcgi_script_name;
    fastcgi_param SCRIPT_NAME "";
    fastcgi_pass unix:/tmp/yourapplication-fcgi.sock;
}
運行 FastCGI 進程

Nginx 和其餘服務器不會載入 FastCGI 應用,你必須本身載入。 Supervisor 能夠管理 FastCGI 進程。 在啓動時你能夠使用其餘 FastCGI 進程管理器或寫一個腳原本運行 .fcgi文件,例如 使用一個 SysV init.d 腳本。若是是臨時使用,你能夠在一個 GNU screen 中運行 .fcgi 腳本。運行細節參見 man screen ,同時請注意這是一個手動啓動方法, 不會在系統重啓時自動啓動:

$ screen
$ /var/www/yourapplication/yourapplication.fcgi
調試

在大多數服務器上, FastCGI 部署難以調試。一般服務器日誌只會告訴你相似 「 premature end of headers 」的內容。爲了調試應用,查找出錯的緣由,你必須切換 到正確的用戶並手動執行應用。

下例假設你的應用是 application.fcgi ,且你的網絡服務用戶爲 www-data:

$ su www-data
$ cd /var/www/yourapplication
$ python application.fcgi
Traceback (most recent call last):
  File "yourapplication.fcgi", line 4, in <module>
ImportError: No module named yourapplication

上面的出錯信息表示 「yourapplication」 不在 python 路徑中。緣由可能有:

  • 使用了相對路徑。在當前工做路徑下路徑出錯。
  • 當前網絡服務器設置未正確設置環境變量。
  • 使用了不一樣的 python 解釋器。

CGI

若是其餘的部署方式都無論用,那麼就只能使用 CGI 了。 CGI 適應於全部主流服務器, 可是其性能稍弱。

這也是在 Google 的 App Engine 使用 Flask 應用的方法,其執行方式相似於 CGI 環境。

當心

請務必把 app.run() 放在 if __name__ == '__main__': 內部或者放在單獨 的文件中,這樣能夠保證它不會被調用。由於,每調用一次就會開啓一個本地 WSGI 服務器。當咱們使用 CGI 或 App Engine 部署應用時,不須要使用本地服務器。

在使用 CGI 時,你還必須確保代碼中不包含任何 print 語句,或者 sys.stdout 被重載,不會寫入 HTTP 響應中。

建立一個 .cgi 文件

首先,你須要建立 CGI 應用文件。咱們把它命名爲 yourapplication.cgi:

#!/usr/bin/python
from wsgiref.handlers import CGIHandler from yourapplication import app CGIHandler().run(app) 
服務器設置

設置服務器一般有兩種方法。一種是把 .cgi 複製爲 cgi-bin (而且使用 mod_rewrite 或其餘相似東西來改寫 URL );另外一種是把服務器直接指向文件。

例如,若是使用 Apache ,那麼能夠把以下內容放入配置中:

ScriptAlias /app /path/to/the/application.cgi 

在共享的網絡服務器上,你可能沒法變更 Apache 配置。在這種狀況下,你能夠使用你 的公共目錄中的 .htaccess 文件。可是 ScriptAlias 指令會失效:

RewriteEngine On RewriteCond %{REQUEST_FILENAME} !-f # Don't interfere with static files RewriteRule ^(.*)$ /path/to/the/application.cgi/$1 [L] 

更多信息參見你所使用的服務器的文檔。

大型應用

如下是一些建議,當你的代碼庫日益壯大或者應用須要規劃時能夠參考。

閱讀源代碼

Werkzeug ( WSGI )和 Jinja (模板)是兩個被普遍使用的工具,而 Flask 起源就是 用於展現如何基於這兩個工具建立你本身的框架。隨着不斷地開發, Flask 被愈來愈多 的人承認了。當你的代碼庫日益壯大時,不該當僅僅是使用 Flask ,而更應當理解它。 因此,請閱讀 Flask 的源代碼吧。 Flask 的源代碼閱讀方便,文檔公開,有利於你直接 使用內部的 API 。 Flask 堅持把上游庫的 API 文檔化,並文檔化本身內部的工具, 所以經過閱讀源代碼,能夠爲你的項目找到更好的切入點。

掛接,擴展

API 文檔隨處可見可用重載、掛接點和 信號 。你能夠定製相似請求 或響應對象的自定義類。請深刻研究你所使用的 API ,並在 Flask 發行版中有哪些能夠 當即使用的可定製部分。請研究你的哪些項目能夠重構爲工具集或 Flask 擴展。你能夠在 社區中發現不少 擴展 。若是找不到滿意的, 那就寫一個你本身的吧。

繼承

Flask 類有許多方法專門爲繼承而設計。你可經過繼承 Flask (參見連接的方法文檔)快速的添加或者定製行爲,並把子類 實例化爲一個應用類。這種方法一樣適用於 應用工廠 。

用中間件包裝

應用調度 一文中詳細闡述瞭如何使用中間件。你能夠引入中間件來包裝你的 Flask 實例,在你的應用和 HTTP 服務器之間的層有所做爲。 Werkzeug 包含許多 中間件 。

派生

若是如下建議都沒有用,那麼直接派生 Flask 吧。 Flask 的主要代碼都在 Werkzeug 和 Jinja2 這兩個庫內。這兩個庫起了主要做用。 Flask 只是把它們粘合在一塊兒而已。對於 一個項目來說,底層框架的切入點很重要。由於若是不重視這一點,那麼框架會變得很是 複雜,勢必帶來陡峭的學習曲線,從而嚇退用戶。

Flask 並不推崇惟一版本。許多人爲了不缺陷,都使用打過補丁或修改過的版本。這個 理念在 Flask 的許可中也有所體現:你沒必要返回你對框架所作的修改。

分支的缺點是大多數擴展都會失效,由於新的框架會使用不一樣的導入名稱。更進一步: 整合上游的變更將會變得十分複雜,上游變更越多,則整合越複雜。所以,建立分支通常 是不得不爲之的最後一招。

專家級的伸縮性

對於大多數網絡應用來講,最複雜的莫過於對於用戶量和數據量提供良好的伸縮性。 Flask 自己具備良好的伸縮性,其伸縮性受限於你的應用代碼、所使用的數據儲存方式、 Python 實現和應用所運行的服務器。

若是服務器數量增長一倍,你的應用性能就增長一倍,那麼就表明伸縮性好。若是伸縮性 很差,那麼即便增長服務器的數量,也不會獲得更好的性能。伸縮性更差的甚至不支持增長 第二臺服務器。

Flask 中惟一影響伸縮性的因素是環境本地代理。Flask 中的環境本地代理能夠被定義爲 線程、進程或 greenlet 。若是你的服務器不支持這些,那麼 Flask 就不能支持全局代理。 可是,當今主流的服務器都支持線程、進程或 greenlet ,以提升併發性。 Flask 的基礎 庫 Werkzeug 對於線程、進程或 greenlet 都可以提供良好的支持。

與社區溝通

無論你的代碼庫是否強大, Flask 開發者老是保持框架的可操做性。若是發現 Flask 有 什麼問題,請當即經過郵件列表或 IRC 與社區進行溝通。對於 Flask 及其擴展的開發都 來講,提高其在大型應用中的功能的最佳途徑是傾聽用戶的心聲。

API 參考

這部分文檔詳細說明某個函數、類或方法。

API

這一章文檔涵蓋了 Flask 全部的接口,對於部分 Flask 所依賴的外部庫,咱們將最 重要的內容呈如今這裏,並提供官方文檔連接。

應用對象

class  flask. Flask (import_namestatic_path=Nonestatic_url_path=None,static_folder='static'template_folder='templates'instance_path=None,instance_relative_config=False)

The flask object implements a WSGI application and acts as the central object. It is passed the name of the module or package of the application. Once it is created it will act as a central registry for the view functions, the URL rules, template configuration and much more.

The name of the package is used to resolve resources from inside the package or the folder the module is contained in depending on if the package parameter resolves to an actual python package (a folder with an __init__.py file inside) or a standard module (just a .py file).

For more information about resource loading, see open_resource().

Usually you create a Flask instance in your main module or in the __init__.py file of your package like this:

from flask import Flask app = Flask(__name__) 

About the First Parameter

The idea of the first parameter is to give Flask an idea what belongs to your application. This name is used to find resources on the file system, can be used by extensions to improve debugging information and a lot more.

So it’s important what you provide there. If you are using a single module,__name__ is always the correct value. If you however are using a package, it’s usually recommended to hardcode the name of your package there.

For example if your application is defined in yourapplication/app.py you should create it with one of the two versions below:

app = Flask('yourapplication') app = Flask(__name__.split('.')[0]) 

Why is that? The application will work even with __name__, thanks to how resources are looked up. However it will make debugging more painful. Certain extensions can make assumptions based on the import name of your application. For example the Flask-SQLAlchemy extension will look for the code in your application that triggered an SQL query in debug mode. If the import name is not properly set up, that debugging information is lost. (For example it would only pick up SQL queries in yourapplication.app and not yourapplication.views.frontend)

New in version 0.7: The static_url_pathstatic_folder, and template_folderparameters were added.

New in version 0.8: The instance_path and instance_relative_config parameters were added.

Parameters:
  • import_name – the name of the application package
  • static_url_path – can be used to specify a different path for the static files on the web. Defaults to the name of thestatic_folder folder.
  • static_folder – the folder with static files that should be served at static_url_path. Defaults to the 'static' folder in the root path of the application.
  • template_folder – the folder that contains the templates that should be used by the application. Defaults to 'templates'folder in the root path of the application.
  • instance_path – An alternative instance path for the application. By default the folder 'instance' next to the package or module is assumed to be the instance path.
  • instance_relative_config – if set to True relative filenames for loading the config are assumed to be relative to the instance path instead of the application root.
add_template_filter (*args**kwargs)

Register a custom template filter. Works exactly like the template_filter()decorator.

Parameters: name – the optional name of the filter, otherwise the function name will be used.
add_template_global (*args**kwargs)

Register a custom template global function. Works exactly like thetemplate_global() decorator.

New in version 0.10.

Parameters: name – the optional name of the global function, otherwise the function name will be used.
add_template_test (*args**kwargs)

Register a custom template test. Works exactly like the template_test()decorator.

New in version 0.10.

Parameters: name – the optional name of the test, otherwise the function name will be used.
add_url_rule (*args**kwargs)

Connects a URL rule. Works exactly like the route() decorator. If a view_func is provided it will be registered with the endpoint.

Basically this example:

@app.route('/') def index(): pass 

Is equivalent to the following:

def index(): pass app.add_url_rule('/', 'index', index) 

If the view_func is not provided you will need to connect the endpoint to a view function like so:

app.view_functions['index'] = index 

Internally route() invokes add_url_rule() so if you want to customize the behavior via subclassing you only need to change this method.

For more information refer to URL Route Registrations.

Changed in version 0.2: view_func parameter added.

Changed in version 0.6: OPTIONS is added automatically as method.

Parameters:
  • rule – the URL rule as string
  • endpoint – the endpoint for the registered URL rule. Flask itself assumes the name of the view function as endpoint
  • view_func – the function to call when serving a request to the provided endpoint
  • options – the options to be forwarded to the underlyingRule object. A change to Werkzeug is handling of method options. methods is a list of methods this rule should be limited to (GETPOST etc.). By default a rule just listens forGET (and implicitly HEAD). Starting with Flask 0.6,OPTIONS is implicitly added and handled by the standard request handling.
after_request (*args**kwargs)

Register a function to be run after each request. Your function must take one parameter, a response_class object and return a new response object or the same (see process_response()).

As of Flask 0.7 this function might not be executed at the end of the request in case an unhandled exception occurred.

after_request_funcs  = None

A dictionary with lists of functions that should be called after each request. The key of the dictionary is the name of the blueprint this function is active for,None for all requests. This can for example be used to open database connections or getting hold of the currently logged in user. To register a function here, use the after_request() decorator.

app_context ()

Binds the application only. For as long as the application is bound to the current context the flask.current_app points to that application. An application context is automatically created when a request context is pushed if necessary.

Example usage:

with app.app_context(): ... 

New in version 0.9.

app_ctx_globals_class

The class that is used for the g instance.

Example use cases for a custom class:

  1. Store arbitrary attributes on flask.g.
  2. Add a property for lazy per-request database connectors.
  3. Return None instead of AttributeError on expected attributes.
  4. Raise exception if an unexpected attr is set, a 「controlled」 flask.g.

In Flask 0.9 this property was called request_globals_class but it was changed in 0.10 to app_ctx_globals_class because the flask.g object is not application context scoped.

New in version 0.10.

alias of _AppCtxGlobals

auto_find_instance_path ()

Tries to locate the instance path if it was not provided to the constructor of the application class. It will basically calculate the path to a folder namedinstance next to your main file or the package.

New in version 0.8.

before_first_request (*args**kwargs)

Registers a function to be run before the first request to this instance of the application.

New in version 0.8.

before_first_request_funcs  = None

A lists of functions that should be called at the beginning of the first request to this instance. To register a function here, use the before_first_request()decorator.

New in version 0.8.

before_request (*args**kwargs)

Registers a function to run before each request.

before_request_funcs  = None

A dictionary with lists of functions that should be called at the beginning of the request. The key of the dictionary is the name of the blueprint this function is active for, None for all requests. This can for example be used to open database connections or getting hold of the currently logged in user. To register a function here, use the before_request() decorator.

blueprints  = None

all the attached blueprints in a dictionary by name. Blueprints can be attached multiple times so this dictionary does not tell you how often they got attached.

New in version 0.7.

config  = None

The configuration dictionary as Config. This behaves exactly like a regular dictionary but supports additional methods to load a config from files.

context_processor (*args**kwargs)

Registers a template context processor function.

create_global_jinja_loader ()

Creates the loader for the Jinja2 environment. Can be used to override just the loader and keeping the rest unchanged. It’s discouraged to override this function. Instead one should override the jinja_loader() function instead.

The global loader dispatches between the loaders of the application and the individual blueprints.

New in version 0.7.

create_jinja_environment ()

Creates the Jinja2 environment based on jinja_options andselect_jinja_autoescape(). Since 0.7 this also adds the Jinja2 globals and filters after initialization. Override this function to customize the behavior.

New in version 0.5.

create_url_adapter (request)

Creates a URL adapter for the given request. The URL adapter is created at a point where the request context is not yet set up so the request is passed explicitly.

New in version 0.6.

Changed in version 0.9: This can now also be called without a request object when the URL adapter is created for the application context.

debug

The debug flag. Set this to True to enable debugging of the application. In debug mode the debugger will kick in when an unhandled exception occurs and the integrated server will automatically reload the application if changes in the code are detected.

This attribute can also be configured from the config with the DEBUGconfiguration key. Defaults to False.

debug_log_format  = '--------------------------------------------------------------------------------\n%(levelname)s in %(module)s [%(pathname)s:%(lineno)d]:\n%(message)s\n--------------------------------------------------------------------------------'

The logging format used for the debug logger. This is only used when the application is in debug mode, otherwise the attached logging handler does the formatting.

New in version 0.3.

default_config  = ImmutableDict({'JSON_AS_ASCII': True, 'USE_X_SENDFILE': False, 'SESSION_COOKIE_PATH': None, 'SESSION_COOKIE_DOMAIN': None, 'SESSION_COOKIE_NAME': 'session', 'LOGGER_NAME': None, 'DEBUG': False, 'SECRET_KEY': None, 'MAX_CONTENT_LENGTH': None, 'APPLICATION_ROOT': None, 'SERVER_NAME': None, 'PREFERRED_URL_SCHEME': 'http', 'JSONIFY_PRETTYPRINT_REGULAR': True, 'TESTING': False, 'PERMANENT_SESSION_LIFETIME': datetime.timedelta(31), 'PROPAGATE_EXCEPTIONS': None, 'TRAP_BAD_REQUEST_ERRORS': False, 'JSON_SORT_KEYS': True, 'SESSION_COOKIE_HTTPONLY': True, 'SEND_FILE_MAX_AGE_DEFAULT': 43200, 'PRESERVE_CONTEXT_ON_EXCEPTION': None, 'SESSION_COOKIE_SECURE': False, 'TRAP_HTTP_EXCEPTIONS': False})

Default configuration parameters.

dispatch_request ()

Does the request dispatching. Matches the URL and returns the return value of the view or error handler. This does not have to be a response object. In order to convert the return value to a proper response object, call make_response().

Changed in version 0.7: This no longer does the exception handling, this code was moved to the new full_dispatch_request().

do_teardown_appcontext (exc=None)

Called when an application context is popped. This works pretty much the same as do_teardown_request() but for the application context.

New in version 0.9.

do_teardown_request (exc=None)

Called after the actual request dispatching and will call every asteardown_request() decorated function. This is not actually called by theFlask object itself but is always triggered when the request context is popped. That way we have a tighter control over certain resources under testing environments.

Changed in version 0.9: Added the exc argument. Previously this was always using the current exception information.

enable_modules  = True

Enable the deprecated module support? This is active by default in 0.7 but will be changed to False in 0.8. With Flask 1.0 modules will be removed in favor of Blueprints

endpoint (*args**kwargs)

A decorator to register a function as an endpoint. Example:

@app.endpoint('example.endpoint') def example(): return "example" 
Parameters: endpoint – the name of the endpoint
error_handler_spec  = None

A dictionary of all registered error handlers. The key is None for error handlers active on the application, otherwise the key is the name of the blueprint. Each key points to another dictionary where they key is the status code of the http exception. The special key None points to a list of tuples where the first item is the class for the instance check and the second the error handler function.

To register a error handler, use the errorhandler() decorator.

errorhandler (*args**kwargs)

A decorator that is used to register a function give a given error code. Example:

@app.errorhandler(404) def page_not_found(error): return 'This page does not exist', 404 

You can also register handlers for arbitrary exceptions:

@app.errorhandler(DatabaseError) def special_exception_handler(error): return 'Database connection failed', 500 

You can also register a function as error handler without using theerrorhandler() decorator. The following example is equivalent to the one above:

def page_not_found(error): return 'This page does not exist', 404 app.error_handler_spec[None][404] = page_not_found 

Setting error handlers via assignments to error_handler_spec however is discouraged as it requires fiddling with nested dictionaries and the special case for arbitrary exception types.

The first None refers to the active blueprint. If the error handler should be application wide None shall be used.

New in version 0.7: One can now additionally also register custom exception types that do not necessarily have to be a subclass of the HTTPException class.

Parameters: code – the code as integer for the handler
extensions  = None

a place where extensions can store application specific state. For example this is where an extension could store database engines and similar things. For backwards compatibility extensions should register themselves like this:

if not hasattr(app, 'extensions'): app.extensions = {} app.extensions['extensionname'] = SomeObject() 

The key must match the name of the flaskext module. For example in case of a 「Flask-Foo」 extension in flaskext.foo, the key would be 'foo'.

New in version 0.7.

full_dispatch_request ()

Dispatches the request and on top of that performs request pre and postprocessing as well as HTTP exception catching and error handling.

New in version 0.7.

get_send_file_max_age (filename)

Provides default cache_timeout for the send_file() functions.

By default, this function returns SEND_FILE_MAX_AGE_DEFAULT from the configuration of current_app.

Static file functions such as send_from_directory() use this function, andsend_file() calls this function on current_app when the given cache_timeout is None. If a cache_timeout is given in send_file(), that timeout is used; otherwise, this method is called.

This allows subclasses to change the behavior when sending files based on the filename. For example, to set the cache timeout for .js files to 60 seconds:

class MyFlask(flask.Flask): def get_send_file_max_age(self, name): if name.lower().endswith('.js'): return 60 return flask.Flask.get_send_file_max_age(self, name) 

New in version 0.9.

got_first_request

This attribute is set to True if the application started handling the first request.

New in version 0.8.

handle_exception (e)

Default exception handling that kicks in when an exception occurs that is not caught. In debug mode the exception will be re-raised immediately, otherwise it is logged and the handler for a 500 internal server error is used. If no such handler exists, a default 500 internal server error message is displayed.

New in version 0.3.

handle_http_exception (e)

Handles an HTTP exception. By default this will invoke the registered error handlers and fall back to returning the exception as response.

New in version 0.3.

handle_url_build_error (errorendpointvalues)

Handle BuildError on url_for().

handle_user_exception (e)

This method is called whenever an exception occurs that should be handled. A special case are HTTPExceptions which are forwarded by this function to thehandle_http_exception() method. This function will either return a response value or reraise the exception with the same traceback.

New in version 0.7.

has_static_folder

This is True if the package bound object’s container has a folder named'static'.

New in version 0.5.

init_jinja_globals ()

Deprecated. Used to initialize the Jinja2 globals.

New in version 0.5.

Changed in version 0.7: This method is deprecated with 0.7. Overridecreate_jinja_environment() instead.

inject_url_defaults (endpointvalues)

Injects the URL defaults for the given endpoint directly into the values dictionary passed. This is used internally and automatically called on URL building.

New in version 0.7.

instance_path  = None

Holds the path to the instance folder.

New in version 0.8.

jinja_env

The Jinja2 environment used to load templates.

jinja_loader

The Jinja loader for this package bound object.

New in version 0.5.

jinja_options  = ImmutableDict({'extensions': ['jinja2.ext.autoescape', 'jinja2.ext.with_']})

Options that are passed directly to the Jinja2 environment.

json_decoder

The JSON decoder class to use. Defaults to JSONDecoder.

New in version 0.10.

alias of JSONDecoder

json_encoder

The JSON encoder class to use. Defaults to JSONEncoder.

New in version 0.10.

alias of JSONEncoder

log_exception (exc_info)

Logs an exception. This is called by handle_exception() if debugging is disabled and right before the handler is called. The default implementation logs the exception as error on the logger.

New in version 0.8.

logger

logging.Logger object for this application. The default configuration is to log to stderr if the application is in debug mode. This logger can be used to (surprise) log messages. Here some examples:

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

New in version 0.3.

logger_name

The name of the logger to use. By default the logger name is the package name passed to the constructor.

New in version 0.4.

make_config (instance_relative=False)

Used to create the config attribute by the Flask constructor. Theinstance_relative parameter is passed in from the constructor of Flask (there named instance_relative_config) and indicates if the config should be relative to the instance path or the root path of the application.

New in version 0.8.

make_default_options_response ()

This method is called to create the default OPTIONS response. This can be changed through subclassing to change the default behavior of OPTIONSresponses.

New in version 0.7.

make_null_session ()

Creates a new instance of a missing session. Instead of overriding this method we recommend replacing the session_interface.

New in version 0.7.

make_response (rv)

Converts the return value from a view function to a real response object that is an instance of response_class.

The following types are allowed for rv:

response_class the object is returned unchanged
str a response object is created with the string as body
unicode a response object is created with the string encoded to utf-8 as body
a WSGI function the function is called as WSGI application and buffered as response object
tuple A tuple in the form (response, status,headers) where response is any of the types defined here, status is a string or an integer andheaders is a list of a dictionary with header values.
Parameters: rv – the return value from the view function

Changed in version 0.9: Previously a tuple was interpreted as the arguments for the response object.

name

The name of the application. This is usually the import name with the difference that it’s guessed from the run file if the import name is main. This name is used as a display name when Flask needs the name of the application. It can be set and overridden to change the value.

New in version 0.8.

open_instance_resource (resourcemode='rb')

Opens a resource from the application’s instance folder (instance_path). Otherwise works like open_resource(). Instance resources can also be opened for writing.

Parameters:
  • resource – the name of the resource. To access resources within subfolders use forward slashes as separator.
  • mode – resource file opening mode, default is ‘rb’.
open_resource (resourcemode='rb')

Opens a resource from the application’s resource folder. To see how this works, consider the following folder structure:

/myapplication.py
/schema.sql
/static
    /style.css
/templates
    /layout.html
    /index.html

If you want to open the schema.sql file you would do the following:

with app.open_resource('schema.sql') as f: contents = f.read() do_something_with(contents) 
Parameters:
  • resource – the name of the resource. To access resources within subfolders use forward slashes as separator.
  • mode – resource file opening mode, default is ‘rb’.
open_session (request)

Creates or opens a new session. Default implementation stores all session data in a signed cookie. This requires that the secret_key is set. Instead of overriding this method we recommend replacing the session_interface.

Parameters: request – an instance of request_class.
permanent_session_lifetime

timedelta which is used to set the expiration date of a permanent session. The default is 31 days which makes a permanent session survive for roughly one month.

This attribute can also be configured from the config with thePERMANENT_SESSION_LIFETIME configuration key. Defaults totimedelta(days=31)

preprocess_request ()

Called before the actual request dispatching and will call every asbefore_request() decorated function. If any of these function returns a value it’s handled as if it was the return value from the view and further request handling is stopped.

This also triggers the url_value_processor() functions before the actualbefore_request() functions are called.

preserve_context_on_exception

Returns the value of the PRESERVE_CONTEXT_ON_EXCEPTIONconfiguration value in case it’s set, otherwise a sensible default is returned.

New in version 0.7.

process_response (response)

Can be overridden in order to modify the response object before it’s sent to the WSGI server. By default this will call all the after_request() decorated functions.

Changed in version 0.5: As of Flask 0.5 the functions registered for after request execution are called in reverse order of registration.

Parameters: response – a response_class object.
Returns: a new response object or the same, has to be an instance ofresponse_class.
propagate_exceptions

Returns the value of the PROPAGATE_EXCEPTIONS configuration value in case it’s set, otherwise a sensible default is returned.

New in version 0.7.

register_blueprint (*args**kwargs)

Registers a blueprint on the application.

New in version 0.7.

register_error_handler (code_or_exceptionf)

Alternative error attach function to the errorhandler() decorator that is more straightforward to use for non decorator usage.

New in version 0.7.

register_module (module**options)

Registers a module with this application. The keyword argument of this function are the same as the ones for the constructor of the Module class and will override the values of the module if provided.

Changed in version 0.7: The module system was deprecated in favor for the blueprint system.

request_class

The class that is used for request objects. See Request for more information.

alias of Request

request_context (environ)

Creates a RequestContext from the given environment and binds it to the current context. This must be used in combination with the with statement because the request is only bound to the current context for the duration of thewith block.

Example usage:

with app.request_context(environ): do_something_with(request) 

The object returned can also be used without the with statement which is useful for working in the shell. The example above is doing exactly the same as this code:

ctx = app.request_context(environ) ctx.push() try: do_something_with(request) finally: ctx.pop() 

Changed in version 0.3: Added support for non-with statement usage and withstatement is now passed the ctx object.

Parameters: environ – a WSGI environment
response_class

The class that is used for response objects. See Response for more information.

alias of Response

route (rule**options)

A decorator that is used to register a view function for a given URL rule. This does the same thing as add_url_rule() but is intended for decorator usage:

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

For more information refer to URL Route Registrations.

Parameters:
  • rule – the URL rule as string
  • endpoint – the endpoint for the registered URL rule. Flask itself assumes the name of the view function as endpoint
  • options – the options to be forwarded to the underlyingRule object. A change to Werkzeug is handling of method options. methods is a list of methods this rule should be limited to (GETPOST etc.). By default a rule just listens forGET (and implicitly HEAD). Starting with Flask 0.6,OPTIONS is implicitly added and handled by the standard request handling.
run (host=Noneport=Nonedebug=None**options)

Runs the application on a local development server. If the debug flag is set the server will automatically reload for code changes and show a debugger in case an exception happened.

If you want to run the application in debug mode, but disable the code execution on the interactive debugger, you can pass use_evalex=False as parameter. This will keep the debugger’s traceback screen active, but disable code execution.

Keep in Mind

Flask will suppress any server error with a generic error page unless it is in debug mode. As such to enable just the interactive debugger without the code reloading, you have to invoke run() with debug=True anduse_reloader=False. Setting use_debugger to True without being in debug mode won’t catch any exceptions because there won’t be any to catch.

Changed in version 0.10: The default port is now picked from the SERVER_NAMEvariable.

Parameters:
  • host – the hostname to listen on. Set this to '0.0.0.0' to have the server available externally as well. Defaults to'127.0.0.1'.
  • port – the port of the webserver. Defaults to 5000 or the port defined in the SERVER_NAME config variable if present.
  • debug – if given, enable or disable debug mode. See debug.
  • options – the options to be forwarded to the underlying Werkzeug server. See werkzeug.serving.run_simple()for more information.
save_session (sessionresponse)

Saves the session if it needs updates. For the default implementation, checkopen_session(). Instead of overriding this method we recommend replacing the session_interface.

Parameters:
secret_key

If a secret key is set, cryptographic components can use this to sign cookies and other things. Set this to a complex random value when you want to use the secure cookie for instance.

This attribute can also be configured from the config with the SECRET_KEYconfiguration key. Defaults to None.

select_jinja_autoescape (filename)

Returns True if autoescaping should be active for the given template name.

New in version 0.5.

send_static_file (filename)

Function used internally to send static files from the static folder to the browser.

New in version 0.5.

The secure cookie uses this for the name of the session cookie.

This attribute can also be configured from the config with theSESSION_COOKIE_NAME configuration key. Defaults to 'session'

session_interface  = <flask.sessions.SecureCookieSessionInterface object>

the session interface to use. By default an instance ofSecureCookieSessionInterface is used here.

New in version 0.8.

should_ignore_error (error)

This is called to figure out if an error should be ignored or not as far as the teardown system is concerned. If this function returns True then the teardown handlers will not be passed the error.

New in version 0.10.

teardown_appcontext (*args**kwargs)

Registers a function to be called when the application context ends. These functions are typically also called when the request context is popped.

Example:

ctx = app.app_context() ctx.push() ... ctx.pop() 

When ctx.pop() is executed in the above example, the teardown functions are called just before the app context moves from the stack of active contexts. This becomes relevant if you are using such constructs in tests.

Since a request context typically also manages an application context it would also be called when you pop a request context.

When a teardown function was called because of an exception it will be passed an error object.

New in version 0.9.

teardown_appcontext_funcs  = None

A list of functions that are called when the application context is destroyed. Since the application context is also torn down if the request ends this is the place to store code that disconnects from databases.

New in version 0.9.

teardown_request (*args**kwargs)

Register a function to be run at the end of each request, regardless of whether there was an exception or not. These functions are executed when the request context is popped, even if not an actual request was performed.

Example:

ctx = app.test_request_context() ctx.push() ... ctx.pop() 

When ctx.pop() is executed in the above example, the teardown functions are called just before the request context moves from the stack of active contexts. This becomes relevant if you are using such constructs in tests.

Generally teardown functions must take every necessary step to avoid that they will fail. If they do execute code that might fail they will have to surround the execution of these code by try/except statements and log occurring errors.

When a teardown function was called because of a exception it will be passed an error object.

Debug Note

In debug mode Flask will not tear down a request on an exception immediately. Instead if will keep it alive so that the interactive debugger can still access it. This behavior can be controlled by the PRESERVE_CONTEXT_ON_EXCEPTIONconfiguration variable.

teardown_request_funcs  = None

A dictionary with lists of functions that are called after each request, even if an exception has occurred. The key of the dictionary is the name of the blueprint this function is active for, None for all requests. These functions are not allowed to modify the request, and their return values are ignored. If an exception occurred while processing the request, it gets passed to each teardown_request function. To register a function here, use the teardown_request() decorator.

New in version 0.7.

template_context_processors  = None

A dictionary with list of functions that are called without argument to populate the template context. The key of the dictionary is the name of the blueprint this function is active for, None for all requests. Each returns a dictionary that the template context is updated with. To register a function here, use thecontext_processor() decorator.

template_filter (*args**kwargs)

A decorator that is used to register custom template filter. You can specify a name for the filter, otherwise the function name will be used. Example:

@app.template_filter() def reverse(s): return s[::-1] 
Parameters: name – the optional name of the filter, otherwise the function name will be used.
template_global (*args**kwargs)

A decorator that is used to register a custom template global function. You can specify a name for the global function, otherwise the function name will be used. Example:

@app.template_global() def double(n): return 2 * n 

New in version 0.10.

Parameters: name – the optional name of the global function, otherwise the function name will be used.
template_test (*args**kwargs)

A decorator that is used to register custom template test. You can specify a name for the test, otherwise the function name will be used. Example:

@app.template_test() def is_prime(n): if n == 2: return True for i in range(2, int(math.ceil(math.sqrt(n))) + 1): if n % i == 0: return False return True 

New in version 0.10.

Parameters: name – the optional name of the test, otherwise the function name will be used.
test_client (use_cookies=True)

Creates a test client for this application. For information about unit testing head over to 測試 Flask 應用.

Note that if you are testing for assertions or exceptions in your application code, you must set app.testing True in order for the exceptions to propagate to the test client. Otherwise, the exception will be handled by the application (not visible to the test client) and the only indication of an AssertionError or other exception will be a 500 status code response to the test client. See the testingattribute. For example:

app.testing = True client = app.test_client() 

The test client can be used in a with block to defer the closing down of the context until the end of the with block. This is useful if you want to access the context locals for testing:

with app.test_client() as c: rv = c.get('/?vodka=42') assert request.args['vodka'] == '42' 

See FlaskClient for more information.

Changed in version 0.4: added support for with block usage for the client.

New in version 0.7: The use_cookies parameter was added as well as the ability to override the client to be used by setting the test_client_class attribute.

test_client_class  = None

the test client that is used with when test_client is used.

New in version 0.7.

test_request_context (*args**kwargs)

Creates a WSGI environment from the given values (seewerkzeug.test.EnvironBuilder() for more information, this function accepts the same arguments).

testing

The testing flag. Set this to True to enable the test mode of Flask extensions (and in the future probably also Flask itself). For example this might activate unittest helpers that have an additional runtime cost which should not be enabled by default.

If this is enabled and PROPAGATE_EXCEPTIONS is not changed from the default it’s implicitly enabled.

This attribute can also be configured from the config with the TESTINGconfiguration key. Defaults to False.

trap_http_exception (e)

Checks if an HTTP exception should be trapped or not. By default this will return False for all exceptions except for a bad request key error ifTRAP_BAD_REQUEST_ERRORS is set to True. It also returns True ifTRAP_HTTP_EXCEPTIONS is set to True.

This is called for all HTTP exceptions raised by a view function. If it returnsTrue for any exception the error handler for this exception is not called and it shows up as regular exception in the traceback. This is helpful for debugging implicitly raised HTTP exceptions.

New in version 0.8.

update_template_context (context)

Update the template context with some commonly used variables. This injects request, session, config and g into the template context as well as everything template context processors want to inject. Note that the as of Flask 0.6, the original values in the context will not be overridden if a context processor decides to return a value with the same key.

Parameters: context – the context as a dictionary that is updated in place to add extra variables.
url_build_error_handlers  = None

A list of functions that are called when url_for() raises a BuildError. Each function registered here is called with errorendpoint and values. If a function returns None or raises a BuildError the next function is tried.

New in version 0.9.

url_default_functions  = None

A dictionary with lists of functions that can be used as URL value preprocessors. The key None here is used for application wide callbacks, otherwise the key is the name of the blueprint. Each of these functions has the chance to modify the dictionary of URL values before they are used as the keyword arguments of the view function. For each function registered this one should also provide a url_defaults() function that adds the parameters automatically again that were removed that way.

New in version 0.7.

url_defaults (*args**kwargs)

Callback function for URL defaults for all view functions of the application. It’s called with the endpoint and values and should update the values passed in place.

url_map  = None

The Map for this instance. You can use this to change the routing converters after the class was created but before any routes are connected. Example:

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) app = Flask(__name__) app.url_map.converters['list'] = ListConverter 
url_rule_class

The rule object to use for URL rules created. This is used by add_url_rule(). Defaults to werkzeug.routing.Rule.

New in version 0.7.

alias of Rule

url_value_preprocessor (*args**kwargs)

Registers a function as URL value preprocessor for all view functions of the application. It’s called before the view functions are called and can modify the url values provided.

url_value_preprocessors  = None

A dictionary with lists of functions that can be used as URL value processor functions. Whenever a URL is built these functions are called to modify the dictionary of values in place. The key None here is used for application wide callbacks, otherwise the key is the name of the blueprint. Each of these functions has the chance to modify the dictionary

New in version 0.7.

use_x_sendfile

Enable this if you want to use the X-Sendfile feature. Keep in mind that the server has to support this. This only affects files sent with the send_file()method.

New in version 0.2.

This attribute can also be configured from the config with theUSE_X_SENDFILE configuration key. Defaults to False.

view_functions  = None

A dictionary of all view functions registered. The keys will be function names which are also used to generate URLs and the values are the function objects themselves. To register a view function, use the route() decorator.

wsgi_app (environstart_response)

The actual WSGI application. This is not implemented in __call__ so that middlewares can be applied without losing a reference to the class. So instead of doing this:

app = MyMiddleware(app) 

It’s a better idea to do this instead:

app.wsgi_app = MyMiddleware(app.wsgi_app) 

Then you still have the original application object around and can continue to call methods on it.

Changed in version 0.7: The behavior of the before and after request callbacks was changed under error conditions and a new callback was added that will always execute at the end of the request, independent on if an error occurred or not. See 回調和錯誤處理.

Parameters:
  • environ – a WSGI environment
  • start_response – a callable accepting a status code, a list of headers and an optional exception context to start the response

Blueprint Objects

class  flask. Blueprint (nameimport_namestatic_folder=None,static_url_path=Nonetemplate_folder=Noneurl_prefix=Nonesubdomain=None,url_defaults=None)

Represents a blueprint. A blueprint is an object that records functions that will be called with the BlueprintSetupState later to register functions or other things on the main application. See 使用藍圖的模塊化應用 for more information.

New in version 0.7.

add_app_template_filter (fname=None)

Register a custom template filter, available application wide. LikeFlask.add_template_filter() but for a blueprint. Works exactly like theapp_template_filter() decorator.

Parameters: name – the optional name of the filter, otherwise the function name will be used.
add_app_template_global (fname=None)

Register a custom template global, available application wide. LikeFlask.add_template_global() but for a blueprint. Works exactly like theapp_template_global() decorator.

New in version 0.10.

Parameters: name – the optional name of the global, otherwise the function name will be used.
add_app_template_test (fname=None)

Register a custom template test, available application wide. LikeFlask.add_template_test() but for a blueprint. Works exactly like theapp_template_test() decorator.

New in version 0.10.

Parameters: name – the optional name of the test, otherwise the function name will be used.
add_url_rule (ruleendpoint=Noneview_func=None**options)

Like Flask.add_url_rule() but for a blueprint. The endpoint for theurl_for() function is prefixed with the name of the blueprint.

after_app_request (f)

Like Flask.after_request() but for a blueprint. Such a function is executed after each request, even if outside of the blueprint.

after_request (f)

Like Flask.after_request() but for a blueprint. This function is only executed after each request that is handled by a function of that blueprint.

app_context_processor (f)

Like Flask.context_processor() but for a blueprint. Such a function is executed each request, even if outside of the blueprint.

app_errorhandler (code)

Like Flask.errorhandler() but for a blueprint. This handler is used for all requests, even if outside of the blueprint.

app_template_filter (name=None)

Register a custom template filter, available application wide. LikeFlask.template_filter() but for a blueprint.

Parameters: name – the optional name of the filter, otherwise the function name will be used.
app_template_global (name=None)

Register a custom template global, available application wide. LikeFlask.template_global() but for a blueprint.

New in version 0.10.

Parameters: name – the optional name of the global, otherwise the function name will be used.
app_template_test (name=None)

Register a custom template test, available application wide. LikeFlask.template_test() but for a blueprint.

New in version 0.10.

Parameters: name – the optional name of the test, otherwise the function name will be used.
app_url_defaults (f)

Same as url_defaults() but application wide.

app_url_value_preprocessor (f)

Same as url_value_preprocessor() but application wide.

before_app_first_request (f)

Like Flask.before_first_request(). Such a function is executed before the first request to the application.

before_app_request (f)

Like Flask.before_request(). Such a function is executed before each request, even if outside of a blueprint.

before_request (f)

Like Flask.before_request() but for a blueprint. This function is only executed before each request that is handled by a function of that blueprint.

context_processor (f)

Like Flask.context_processor() but for a blueprint. This function is only executed for requests handled by a blueprint.

endpoint (endpoint)

Like Flask.endpoint() but for a blueprint. This does not prefix the endpoint with the blueprint name, this has to be done explicitly by the user of this method. If the endpoint is prefixed with a . it will be registered to the current blueprint, otherwise it’s an application independent endpoint.

errorhandler (code_or_exception)

Registers an error handler that becomes active for this blueprint only. Please be aware that routing does not happen local to a blueprint so an error handler for 404 usually is not handled by a blueprint unless it is caused inside a view function. Another special case is the 500 internal server error which is always looked up from the application.

Otherwise works as the errorhandler() decorator of the Flask object.

get_send_file_max_age (filename)

Provides default cache_timeout for the send_file() functions.

By default, this function returns SEND_FILE_MAX_AGE_DEFAULT from the configuration of current_app.

Static file functions such as send_from_directory() use this function, andsend_file() calls this function on current_app when the given cache_timeout is None. If a cache_timeout is given in send_file(), that timeout is used; otherwise, this method is called.

This allows subclasses to change the behavior when sending files based on the filename. For example, to set the cache timeout for .js files to 60 seconds:

class MyFlask(flask.Flask): def get_send_file_max_age(self, name): if name.lower().endswith('.js'): return 60 return flask.Flask.get_send_file_max_age(self, name) 

New in version 0.9.

has_static_folder

This is True if the package bound object’s container has a folder named'static'.

New in version 0.5.

jinja_loader

The Jinja loader for this package bound object.

New in version 0.5.

make_setup_state (appoptionsfirst_registration=False)

Creates an instance of BlueprintSetupState() object that is later passed to the register callback functions. Subclasses can override this to return a subclass of the setup state.

open_resource (resourcemode='rb')

Opens a resource from the application’s resource folder. To see how this works, consider the following folder structure:

/myapplication.py
/schema.sql
/static
    /style.css
/templates
    /layout.html
    /index.html

If you want to open the schema.sql file you would do the following:

with app.open_resource('schema.sql') as f: contents = f.read() do_something_with(contents) 
Parameters:
  • resource – the name of the resource. To access resources within subfolders use forward slashes as separator.
  • mode – resource file opening mode, default is ‘rb’.
record (func)

Registers a function that is called when the blueprint is registered on the application. This function is called with the state as argument as returned by the make_setup_state() method.

record_once (func)

Works like record() but wraps the function in another function that will ensure the function is only called once. If the blueprint is registered a second time on the application, the function passed is not called.

register (appoptionsfirst_registration=False)

Called by Flask.register_blueprint() to register a blueprint on the application. This can be overridden to customize the register behavior. Keyword arguments from register_blueprint() are directly forwarded to this method in the options dictionary.

route (rule**options)

Like Flask.route() but for a blueprint. The endpoint for the url_for()function is prefixed with the name of the blueprint.

send_static_file (filename)

Function used internally to send static files from the static folder to the browser.

New in version 0.5.

teardown_app_request (f)

Like Flask.teardown_request() but for a blueprint. Such a function is executed when tearing down each request, even if outside of the blueprint.

teardown_request (f)

Like Flask.teardown_request() but for a blueprint. This function is only executed when tearing down requests handled by a function of that blueprint. Teardown request functions are executed when the request context is popped, even when no actual request was performed.

url_defaults (f)

Callback function for URL defaults for this blueprint. It’s called with the endpoint and values and should update the values passed in place.

url_value_preprocessor (f)

Registers a function as URL value preprocessor for this blueprint. It’s called before the view functions are called and can modify the url values provided.

Incoming Request Data

class  flask. Request (environpopulate_request=Trueshallow=False)

The request object used by default in Flask. Remembers the matched endpoint and view arguments.

It is what ends up as request. If you want to replace the request object used you can subclass this and set request_class to your subclass.

The request object is a Request subclass and provides all of the attributes Werkzeug defines plus a few Flask specific ones.

form

MultiDict with the parsed form data from POST or PUT requests. Please keep in mind that file uploads will not end up here, but instead in the filesattribute.

args

MultiDict with the parsed contents of the query string. (The part in the URL after the question mark).

values

CombinedMultiDict with the contents of both form and args.

cookies

dict with the contents of all cookies transmitted with the request.

stream

If the incoming form data was not encoded with a known mimetype the data is stored unmodified in this stream for consumption. Most of the time it is a better idea to use data which will give you that data as a string. The stream only returns the data once.

headers

The incoming request headers as a dictionary like object.

data

Contains the incoming request data as string in case it came with a mimetype Flask does not handle.

files

MultiDict with files uploaded as part of a POST or PUT request. Each file is stored as FileStorage object. It basically behaves like a standard file object you know from Python, with the difference that it also has a save() function that can store the file on the filesystem.

environ

The underlying WSGI environment.

method

The current request method (POSTGET etc.)

path
script_root
url
base_url
url_root

Provides different ways to look at the current URL. Imagine your application is listening on the following URL:

http://www.example.com/myapplication

And a user requests the following URL:

http://www.example.com/myapplication/page.html?x=y

In this case the values of the above mentioned attributes would be the following:

path /page.html
script_root /myapplication
base_url http://www.example.com/myapplication/page.html
url http://www.example.com/myapplication/page.html?x=y
url_root http://www.example.com/myapplication/
is_xhr

True if the request was triggered via a JavaScript XMLHttpRequest. This only works with libraries that support the X-Requested-With header and set it toXMLHttpRequest. Libraries that do that are prototype, jQuery and Mochikit and probably some more.

blueprint

The name of the current blueprint

endpoint

The endpoint that matched the request. This in combination with view_argscan be used to reconstruct the same or a modified URL. If an exception happened when matching, this will be None.

get_json (force=Falsesilent=Falsecache=True)

Parses the incoming JSON request data and returns it. If parsing fails theon_json_loading_failed() method on the request object will be invoked. By default this function will only load the json data if the mimetype isapplication/json but this can be overriden by the force parameter.

Parameters:
  • force – if set to True the mimetype is ignored.
  • silent – if set to False this method will fail silently and return False.
  • cache – if set to True the parsed JSON data is remembered on the request.
json

If the mimetype is application/json this will contain the parsed JSON data. Otherwise this will be None.

The get_json() method should be used instead.

max_content_length

Read-only view of the MAX_CONTENT_LENGTH config key.

module

The name of the current module if the request was dispatched to an actual module. This is deprecated functionality, use blueprints instead.

on_json_loading_failed (e)

Called if decoding of the JSON data failed. The return value of this method is used by get_json() when an error occurred. The default implementation just raises a BadRequest exception.

Changed in version 0.10: Removed buggy previous behavior of generating a random JSON response. If you want that behavior back you can trivially add it by subclassing.

New in version 0.8.

routing_exception  = None

if matching the URL failed, this is the exception that will be raised / was raised as part of the request handling. This is usually a NotFound exception or something similar.

url_rule  = None

the internal URL rule that matched the request. This can be useful to inspect which methods are allowed for the URL from a before/after handler (request.url_rule.methods) etc.

New in version 0.6.

view_args  = None

a dict of view arguments that matched the request. If an exception happened when matching, this will be None.

class  flask. request

To access incoming request data, you can use the global request object. Flask parses incoming request data for you and gives you access to it through that global object. Internally Flask makes sure that you always get the correct data for the active thread if you are in a multithreaded environment.

This is a proxy. See 關於代理 for more information.

The request object is an instance of a Request subclass and provides all of the attributes Werkzeug defines. This just shows a quick overview of the most important ones.

Response Objects

class  flask. Response (response=Nonestatus=Noneheaders=None,mimetype=Nonecontent_type=Nonedirect_passthrough=False)

The response object that is used by default in Flask. Works like the response object from Werkzeug but is set to have an HTML mimetype by default. Quite often you don’t have to create this object yourself because make_response() will take care of that for you.

If you want to replace the response object used you can subclass this and setresponse_class to your subclass.

headers

Headers object representing the response headers.

status

A string with a response status.

status_code

The response status as integer.

data

A descriptor that calls get_data() and set_data(). This should not be used and will eventually get deprecated.

mimetype

The mimetype (content type without charset etc.)

Sets a cookie. The parameters are the same as in the cookie Morsel object in the Python standard library but it accepts unicode data, too.

Parameters:
  • key – the key (name) of the cookie to be set.
  • value – the value of the cookie.
  • max_age – should be a number of seconds, or None(default) if the cookie should last only as long as the client’s browser session.
  • expires – should be a datetime object or UNIX timestamp.
  • domain – if you want to set a cross-domain cookie. For example, domain=".example.com" will set a cookie that is readable by the domain www.example.com,foo.example.com etc. Otherwise, a cookie will only be readable by the domain that set it.
  • path – limits the cookie to a given path, per default it will span the whole domain.

Sessions

If you have the Flask.secret_key set you can use sessions in Flask applications. A session basically makes it possible to remember information from one request to another. The way Flask does this is by using a signed cookie. So the user can look at the session contents, but not modify it unless they know the secret key, so make sure to set that to something complex and unguessable.

To access the current session you can use the session object:

class  flask. session

The session object works pretty much like an ordinary dict, with the difference that it keeps track on modifications.

This is a proxy. See 關於代理 for more information.

The following attributes are interesting:

new

True if the session is new, False otherwise.

modified

True if the session object detected a modification. Be advised that modifications on mutable structures are not picked up automatically, in that situation you have to explicitly set the attribute to True yourself. Here an example:

# this change is not picked up because a mutable object (here
# a list) is changed. session['objects'].append(42) # so mark it as modified yourself session.modified = True 
permanent

If set to True the session lives for permanent_session_lifetime seconds. The default is 31 days. If set to False (which is the default) the session will be deleted when the user closes the browser.

Session Interface

New in version 0.8.

The session interface provides a simple way to replace the session implementation that Flask is using.

class  flask.sessions. SessionInterface

The basic interface you have to implement in order to replace the default session interface which uses werkzeug’s securecookie implementation. The only methods you have to implement are open_session() and save_session(), the others have useful defaults which you don’t need to change.

The session object returned by the open_session() method has to provide a dictionary like interface plus the properties and methods from the SessionMixin. We recommend just subclassing a dict and adding that mixin:

class Session(dict, SessionMixin): pass 

If open_session() returns None Flask will call into make_null_session() to create a session that acts as replacement if the session support cannot work because some requirement is not fulfilled. The default NullSession class that is created will complain that the secret key was not set.

To replace the session interface on an application all you have to do is to assignflask.Flask.session_interface:

app = Flask(__name__) app.session_interface = MySessionInterface() 

New in version 0.8.

Helpful helper method that returns the cookie domain that should be used for the session cookie if session cookies are used.

Returns True if the session cookie should be httponly. This currently just returns the value of the SESSION_COOKIE_HTTPONLY config var.

Returns the path for which the cookie should be valid. The default implementation uses the value from the SESSION_COOKIE_PATH`` config var if it’s set, and falls back to APPLICATION_ROOT or uses / if it’s None.

Returns True if the cookie should be secure. This currently just returns the value of the SESSION_COOKIE_SECURE setting.

get_expiration_time (appsession)

A helper method that returns an expiration date for the session or None if the session is linked to the browser session. The default implementation returns now + the permanent session lifetime configured on the application.

is_null_session (obj)

Checks if a given object is a null session. Null sessions are not asked to be saved.

This checks if the object is an instance of null_session_class by default.

make_null_session (app)

Creates a null session which acts as a replacement object if the real session support could not be loaded due to a configuration error. This mainly aids the user experience because the job of the null session is to still support lookup without complaining but modifications are answered with a helpful error message of what failed.

This creates an instance of null_session_class by default.

null_session_class

make_null_session() will look here for the class that should be created when a null session is requested. Likewise the is_null_session() method will perform a typecheck against this type.

alias of NullSession

open_session (apprequest)

This method has to be implemented and must either return None in case the loading failed because of a configuration error or an instance of a session object which implements a dictionary like interface + the methods and attributes onSessionMixin.

pickle_based  = False

A flag that indicates if the session interface is pickle based. This can be used by flask extensions to make a decision in regards to how to deal with the session object.

New in version 0.10.

save_session (appsessionresponse)

This is called for actual sessions returned by open_session() at the end of the request. This is still called during a request context so if you absolutely need access to the request you can do that.

class  flask.sessions. SecureCookieSessionInterface

The default session interface that stores sessions in signed cookies through theitsdangerous module.

static  digest_method ()

the hash function to use for the signature. The default is sha1

key_derivation  = 'hmac'

the name of the itsdangerous supported key derivation. The default is hmac.

salt  = 'cookie-session'

the salt that should be applied on top of the secret key for the signing of cookie based sessions.

serializer  = <flask.sessions.TaggedJSONSerializer object>

A python serializer for the payload. The default is a compact JSON derived serializer with support for some extra Python types such as datetime objects or tuples.

session_class

alias of SecureCookieSession

class  flask.sessions. SecureCookieSession (initial=None)

Baseclass for sessions based on signed cookies.

class  flask.sessions. NullSession (initial=None)

Class used to generate nicer error messages if sessions are not available. Will still allow read-only access to the empty session but fail on setting.

class  flask.sessions. SessionMixin

Expands a basic dictionary with an accessors that are expected by Flask extensions and users for the session.

modified  = True

for some backends this will always be True, but some backends will default this to false and detect changes in the dictionary for as long as changes do not happen on mutable structures in the session. The default mixin implementation just hardcodes True in.

new  = False

some session backends can tell you if a session is new, but that is not necessarily guaranteed. Use with caution. The default mixin implementation just hardcodes False in.

permanent

this reflects the '_permanent' key in the dict.

flask.sessions. session_json_serializer  = <flask.sessions.TaggedJSONSerializer object>

A customized JSON serializer that supports a few extra types that we take for granted when serializing (tuples, markup objects, datetime).

This object provides dumping and loading methods similar to simplejson but it also tags certain builtin Python objects that commonly appear in sessions. Currently the following extended values are supported in the JSON it dumps:

Notice

The PERMANENT_SESSION_LIFETIME config key can also be an integer starting with Flask 0.8. Either catch this down yourself or use the permanent_session_lifetimeattribute on the app which converts the result to an integer automatically.

Test Client

class  flask.testing. FlaskClient (applicationresponse_wrapper=None,use_cookies=Trueallow_subdomain_redirects=False)

Works like a regular Werkzeug test client but has some knowledge about how Flask works to defer the cleanup of the request context stack to the end of a with body when used in a with statement. For general information about how to use this class refer to werkzeug.test.Client.

Basic usage is outlined in the 測試 Flask 應用 chapter.

session_transaction (*args**kwds)

When used in combination with a with statement this opens a session transaction. This can be used to modify the session that the test client uses. Once the with block is left the session is stored back.

with client.session_transaction() as session:
session[‘value’] = 42

Internally this is implemented by going through a temporary test request context and since session handling could depend on request variables this function accepts the same arguments as test_request_context() which are directly passed through.

Application Globals

To share data that is valid for one request only from one function to another, a global variable is not good enough because it would break in threaded environments. Flask provides you with a special object that ensures it is only valid for the active request and that will return different values for each request. In a nutshell: it does the right thing, like it does for request and session.

flask. g

Just store on this whatever you want. For example a database connection or the user that is currently logged in.

Starting with Flask 0.10 this is stored on the application context and no longer on the request context which means it becomes available if only the application context is bound and not yet a request. This is especially useful when combined with the 僞造資源和環境 pattern for testing.

Additionally as of 0.10 you can use the get() method to get an attribute or None(or the second argument) if it’s not set. These two usages are now equivalent:

user = getattr(flask.g, 'user', None) user = flask.g.get('user', None) 

It’s now also possible to use the in operator on it to see if an attribute is defined and it yields all keys on iteration.

This is a proxy. See 關於代理 for more information.

Useful Functions and Classes

flask. current_app

Points to the application handling the request. This is useful for extensions that want to support multiple applications running side by side. This is powered by the application context and not by the request context, so you can change the value of this proxy by using the app_context() method.

This is a proxy. See 關於代理 for more information.

flask. has_request_context ()

If you have code that wants to test if a request context is there or not this function can be used. For instance, you may want to take advantage of request information if the request object is available, but fail silently if it is unavailable.

class User(db.Model): def __init__(self, username, remote_addr=None): self.username = username if remote_addr is None and has_request_context(): remote_addr = request.remote_addr self.remote_addr = remote_addr 

Alternatively you can also just test any of the context bound objects (such asrequest or g for truthness):

class User(db.Model): def __init__(self, username, remote_addr=None): self.username = username if remote_addr is None and request: remote_addr = request.remote_addr self.remote_addr = remote_addr 

New in version 0.7.

flask. copy_current_request_context (f)

A helper function that decorates a function to retain the current request context. This is useful when working with greenlets. The moment the function is decorated a copy of the request context is created and then pushed when the function is called.

Example:

import gevent from flask import copy_current_request_context @app.route('/') def index(): @copy_current_request_context def do_some_work(): # do some work here, it can access flask.request like you # would otherwise in the view function. ... gevent.spawn(do_some_work) return 'Regular response' 

New in version 0.10.

flask. has_app_context ()

Works like has_request_context() but for the application context. You can also just do a boolean check on the current_app object instead.

New in version 0.9.

flask. url_for (endpoint**values)

Generates a URL to the given endpoint with the method provided.

Variable arguments that are unknown to the target endpoint are appended to the generated URL as query arguments. If the value of a query argument is None, the whole pair is skipped. In case blueprints are active you can shortcut references to the same blueprint by prefixing the local endpoint with a dot (.).

This will reference the index function local to the current blueprint:

url_for('.index') 

For more information, head over to the Quickstart.

To integrate applications, Flask has a hook to intercept URL build errors throughFlask.build_error_handler. The url_for function results in a BuildErrorwhen the current app does not have a URL for the given endpoint and values. When it does, the current_app calls its build_error_handler if it is not None, which can return a string to use as the result of url_for (instead of url_for‘s default to raise the BuildError exception) or re-raise the exception. An example:

def external_url_handler(error, endpoint, **values): "Looks up an external URL when `url_for` cannot build a URL." # This is an example of hooking the build_error_handler. # Here, lookup_url is some utility function you've built # which looks up the endpoint in some external URL registry. url = lookup_url(endpoint, **values) if url is None: # External lookup did not have a URL. # Re-raise the BuildError, in context of original traceback. exc_type, exc_value, tb = sys.exc_info() if exc_value is error: raise exc_type, exc_value, tb else: raise error # url_for will use this result, instead of raising BuildError. return url app.build_error_handler = external_url_handler 

Here, error is the instance of BuildError, and endpoint and **values are the arguments passed into url_for. Note that this is for building URLs outside the current application, and not for handling 404 NotFound errors.

New in version 0.10: The _scheme parameter was added.

New in version 0.9: The _anchor and _method parameters were added.

New in version 0.9: Calls Flask.handle_build_error() on BuildError.

Parameters:
  • endpoint – the endpoint of the URL (name of the function)
  • values – the variable arguments of the URL rule
  • _external – if set to True, an absolute URL is generated. Server address can be changed via SERVER_NAMEconfiguration variable which defaults to localhost.
  • _scheme – a string specifying the desired URL scheme. The_external parameter must be set to True or a ValueError is raised.
  • _anchor – if provided this is added as anchor to the URL.
  • _method – if provided this explicitly specifies an HTTP method.
flask. abort (code)

Raises an HTTPException for the given status code. For example to abort request handling with a page not found exception, you would call abort(404).

Parameters: code – the HTTP error code.
flask. redirect (locationcode=302)

Return a response object (a WSGI application) that, if called, redirects the client to the target location. Supported codes are 301, 302, 303, 305, and 307. 300 is not supported because it’s not a real redirect and 304 because it’s the answer for a request with a request with defined If-Modified-Since headers.

New in version 0.6: The location can now be a unicode string that is encoded using the iri_to_uri() function.

Parameters:
  • location – the location the response should redirect to.
  • code – the redirect status code. defaults to 302.
flask. make_response (*args)

Sometimes it is necessary to set additional headers in a view. Because views do not have to return response objects but can return a value that is converted into a response object by Flask itself, it becomes tricky to add headers to it. This function can be called instead of using a return and you will get a response object which you can use to attach headers.

If view looked like this and you want to add a new header:

def index(): return render_template('index.html', foo=42) 

You can now do something like this:

def index(): response = make_response(render_template('index.html', foo=42)) response.headers['X-Parachutes'] = 'parachutes are cool' return response 

This function accepts the very same arguments you can return from a view function. This for example creates a response with a 404 error code:

response = make_response(render_template('not_found.html'), 404) 

The other use case of this function is to force the return value of a view function into a response which is helpful with view decorators:

response = make_response(view_function()) response.headers['X-Parachutes'] = 'parachutes are cool' 

Internally this function does the following things:

New in version 0.6.

flask. after_this_request (f)

Executes a function after this request. This is useful to modify response objects. The function is passed the response object and has to return the same or a new one.

Example:

@app.route('/') def index(): @after_this_request def add_header(response): response.headers['X-Foo'] = 'Parachute' return response return 'Hello World!' 

This is more useful if a function other than the view function wants to modify a response. For instance think of a decorator that wants to add some headers without converting the return value into a response object.

New in version 0.9.

flask. send_file (filename_or_fpmimetype=Noneas_attachment=False,attachment_filename=Noneadd_etags=Truecache_timeout=None,conditional=False)

Sends the contents of a file to the client. This will use the most efficient method available and configured. By default it will try to use the WSGI server’s file_wrapper support. Alternatively you can set the application’s use_x_sendfileattribute to True to directly emit an X-Sendfile header. This however requires support of the underlying webserver for X-Sendfile.

By default it will try to guess the mimetype for you, but you can also explicitly provide one. For extra security you probably want to send certain files as attachment (HTML for instance). The mimetype guessing requires a filename or anattachment_filename to be provided.

Please never pass filenames to this function from user sources without checking them first. Something like this is usually sufficient to avoid security problems:

if '..' in filename or filename.startswith('/'): abort(404) 

New in version 0.2.

New in version 0.5: The add_etagscache_timeout and conditional parameters were added. The default behavior is now to attach etags.

Changed in version 0.7: mimetype guessing and etag support for file objects was deprecated because it was unreliable. Pass a filename if you are able to, otherwise attach an etag yourself. This functionality will be removed in Flask 1.0

Changed in version 0.9: cache_timeout pulls its default from application config, when None.

Parameters:
  • filename_or_fp – the filename of the file to send. This is relative to the root_path if a relative path is specified. Alternatively a file object might be provided in which case X-Sendfile might not work and fall back to the traditional method. Make sure that the file pointer is positioned at the start of data to send before calling send_file().
  • mimetype – the mimetype of the file if provided, otherwise auto detection happens.
  • as_attachment – set to True if you want to send this file with a Content-Disposition: attachment header.
  • attachment_filename – the filename for the attachment if it differs from the file’s filename.
  • add_etags – set to False to disable attaching of etags.
  • conditional – set to True to enable conditional responses.
  • cache_timeout – the timeout in seconds for the headers. When None (default), this value is set byget_send_file_max_age() of current_app.
flask. send_from_directory (directoryfilename**options)

Send a file from a given directory with send_file(). This is a secure way to quickly expose static files from an upload folder or something similar.

Example usage:

@app.route('/uploads/<path:filename>') def download_file(filename): return send_from_directory(app.config['UPLOAD_FOLDER'], filename, as_attachment=True) 

Sending files and Performance

It is strongly recommended to activate either X-Sendfile support in your webserver or (if no authentication happens) to tell the webserver to serve files for the given path on its own without calling into the web application for improved performance.

New in version 0.5.

Parameters:
  • directory – the directory where all the files are stored.
  • filename – the filename relative to that directory to download.
  • options – optional keyword arguments that are directly forwarded to send_file().
flask. safe_join (directoryfilename)

Safely join directory and filename.

Example usage:

@app.route('/wiki/<path:filename>') def wiki_page(filename): filename = safe_join(app.config['WIKI_FOLDER'], filename) with open(filename, 'rb') as fd: content = fd.read() # Read and process the file content... 
Parameters:
  • directory – the base directory.
  • filename – the untrusted filename relative to that directory.
Raises:

NotFound if the resulting path would fall out of directory.

flask. escape (s) → markup

Convert the characters &, <, >, ‘, and 」 in string s to HTML-safe sequences. Use this if you need to display text that might contain such characters in HTML. Marks return value as markup string.

class  flask. Markup

Marks a string as being safe for inclusion in HTML/XML output without needing to be escaped. This implements the __html__ interface a couple of frameworks and web applications use. Markup is a direct subclass of unicode and provides all the methods of unicode just that it escapes arguments passed and always returnsMarkup.

The escape function returns markup objects so that double escaping can’t happen.

The constructor of the Markup class can be used for three different things: When passed an unicode object it’s assumed to be safe, when passed an object with an HTML representation (has an __html__ method) that representation is used, otherwise the object passed is converted into a unicode string and then assumed to be safe:

>>> Markup("Hello <em>World</em>!") Markup(u'Hello <em>World</em>!') >>> class Foo(object): ... def __html__(self): ... return '<a href="#">foo</a>' ... >>> Markup(Foo()) Markup(u'<a href="#">foo</a>') 

If you want object passed being always treated as unsafe you can use the escape()classmethod to create a Markup object:

>>> Markup.escape("Hello <em>World</em>!") Markup(u'Hello &lt;em&gt;World&lt;/em&gt;!') 

Operations on a markup string are markup aware which means that all arguments are passed through the escape() function:

>>> em = Markup("<em>%s</em>") >>> em % "foo & bar" Markup(u'<em>foo &amp; bar</em>') >>> strong = Markup("<strong>%(text)s</strong>") >>> strong % {'text': '<blink>hacker here</blink>'} Markup(u'<strong>&lt;blink&gt;hacker here&lt;/blink&gt;</strong>') >>> Markup("<em>Hello</em> ") + "<foo>" Markup(u'<em>Hello</em> &lt;foo&gt;') 
classmethod  escape (s)

Escape the string. Works like escape() with the difference that for subclasses of Markup this function would return the correct subclass.

striptags ()

Unescape markup into an text_type string and strip all tags. This also resolves known HTML4 and XHTML entities. Whitespace is normalized to one:

>>> Markup("Main &raquo; <em>About</em>").striptags() u'Main \xbb About' 
unescape ()

Unescape markup again into an text_type string. This also resolves known HTML4 and XHTML entities:

>>> Markup("Main &raquo; <em>About</em>").unescape() u'Main \xbb <em>About</em>' 

Message Flashing

flask. flash (messagecategory='message')

Flashes a message to the next request. In order to remove the flashed message from the session and to display it to the user, the template has to callget_flashed_messages().

Changed in version 0.3: category parameter added.

Parameters:
  • message – the message to be flashed.
  • category – the category for the message. The following values are recommended: 'message' for any kind of message, 'error'for errors, 'info' for information messages and 'warning' for warnings. However any kind of string can be used as category.
flask. get_flashed_messages (with_categories=Falsecategory_filter=[])

Pulls all flashed messages from the session and returns them. Further calls in the same request to the function will return the same messages. By default just the messages are returned, but when with_categories is set to True, the return value will be a list of tuples in the form (category, message) instead.

Filter the flashed messages to one or more categories by providing those categories in category_filter. This allows rendering categories in separate html blocks. Thewith_categories and category_filter arguments are distinct:

  • with_categories controls whether categories are returned with message text (True gives a tuple, where False gives just the message text).
  • category_filter filters the messages down to only those matching the provided categories.

See 消息閃現 for examples.

Changed in version 0.3: with_categories parameter added.

Changed in version 0.9: category_filter parameter added.

Parameters:
  • with_categories – set to True to also receive categories.
  • category_filter – whitelist of categories to limit return values

JSON Support

Flask uses simplejson for the JSON implementation. Since simplejson is provided both by the standard library as well as extension Flask will try simplejson first and then fall back to the stdlib json module. On top of that it will delegate access to the current application’s JSOn encoders and decoders for easier customization.

So for starters instead of doing:

try: import simplejson as json except ImportError: import json 

You can instead just do this:

from flask import json 

For usage examples, read the json documentation in the standard lirbary. The following extensions are by default applied to the stdlib’s JSON module:

  1. datetime objects are serialized as RFC 822 strings.
  2. Any object with an __html__ method (like Markup) will ahve that method called and then the return value is serialized as string.

The htmlsafe_dumps() function of this json module is also available as filter called|tojson in Jinja2. Note that inside script tags no escaping must take place, so make sure to disable escaping with |safe if you intend to use it inside script tags unless you are using Flask 0.10 which implies that:

<script type=text/javascript> doSomethingWith({{ user.username|tojson|safe }}); </script> 
flask.json. jsonify (*args**kwargs)

Creates a Response with the JSON representation of the given arguments with anapplication/json mimetype. The arguments to this function are the same as to thedict constructor.

Example usage:

from flask import jsonify @app.route('/_get_current_user') def get_current_user(): return jsonify(username=g.user.username, email=g.user.email, id=g.user.id) 

This will send a JSON response like this to the browser:

{
    "username": "admin", "email": "admin@localhost", "id": 42 } 

For security reasons only objects are supported toplevel. For more information about this, have a look at JSON 安全.

This function’s response will be pretty printed if it was not requested with X-Requested-With: XMLHttpRequest to simplify debugging unless theJSONIFY_PRETTYPRINT_REGULAR config parameter is set to false.

New in version 0.2.

flask.json. dumps (obj**kwargs)

Serialize obj to a JSON formatted str by using the application’s configured encoder (json_encoder) if there is an application on the stack.

This function can return unicode strings or ascii-only bytestrings by default which coerce into unicode strings automatically. That behavior by default is controlled by the JSON_AS_ASCII configuration variable and can be overriden by the simplejsonensure_ascii parameter.

flask.json. dump (objfp**kwargs)

Like dumps() but writes into a file object.

flask.json. loads (s**kwargs)

Unserialize a JSON object from a string s by using the application’s configured decoder (json_decoder) if there is an application on the stack.

flask.json. load (fp**kwargs)

Like loads() but reads from a file object.

class  flask.json. JSONEncoder (skipkeys=Falseensure_ascii=True,check_circular=Trueallow_nan=Truesort_keys=Falseindent=None,separators=Noneencoding='utf-8'default=None)

The default Flask JSON encoder. This one extends the default simplejson encoder by also supporting datetime objects, UUID as well as Markup objects which are serialized as RFC 822 datetime strings (same as the HTTP date format). In order to support more data types override the default() method.

default (o)

Implement this method in a subclass such that it returns a serializable object for o, or calls the base implementation (to raise a TypeError).

For example, to support arbitrary iterators, you could implement default like this:

def default(self, o): try: iterable = iter(o) except TypeError: pass else: return list(iterable) return JSONEncoder.default(self, o) 
class  flask.json. JSONDecoder (encoding=Noneobject_hook=None,parse_float=Noneparse_int=Noneparse_constant=Nonestrict=True,object_pairs_hook=None)

The default JSON decoder. This one does not change the behavior from the default simplejson encoder. Consult the json documentation for more information. This decoder is not only used for the load functions of this module but also Request.

Template Rendering

flask. render_template (template_name_or_list**context)

Renders a template from the template folder with the given context.

Parameters:
  • template_name_or_list – the name of the template to be rendered, or an iterable with template names the first one existing will be rendered
  • context – the variables that should be available in the context of the template.
flask. render_template_string (source**context)

Renders a template from the given template source string with the given context.

Parameters:
  • source – the sourcecode of the template to be rendered
  • context – the variables that should be available in the context of the template.
flask. get_template_attribute (template_nameattribute)

Loads a macro (or variable) a template exports. This can be used to invoke a macro from within Python code. If you for example have a template named _cider.htmlwith the following contents:

{% macro hello(name) %}Hello {{ name }}!{% endmacro %} 

You can access this from Python code like this:

hello = get_template_attribute('_cider.html', 'hello') return hello('World') 

New in version 0.2.

Parameters:
  • template_name – the name of the template
  • attribute – the name of the variable of macro to access

Configuration

class  flask. Config (root_pathdefaults=None)

Works exactly like a dict but provides ways to fill it from files or special dictionaries. There are two common patterns to populate the config.

Either you can fill the config from a config file:

app.config.from_pyfile('yourconfig.cfg') 

Or alternatively you can define the configuration options in the module that callsfrom_object() or provide an import path to a module that should be loaded. It is also possible to tell it to use the same module and with that provide the configuration values just before the call:

DEBUG = True SECRET_KEY = 'development key' app.config.from_object(__name__) 

In both cases (loading from any Python file or loading from modules), only uppercase keys are added to the config. This makes it possible to use lowercase values in the config file for temporary values that are not added to the config or to define the config keys in the same file that implements the application.

Probably the most interesting way to load configurations is from an environment variable pointing to a file:

app.config.from_envvar('YOURAPPLICATION_SETTINGS') 

In this case before launching the application you have to set this environment variable to the file you want to use. On Linux and OS X use the export statement:

export YOURAPPLICATION_SETTINGS='/path/to/config/file'

On windows use set instead.

Parameters:
  • root_path – path to which files are read relative from. When the config object is created by the application, this is the application’s root_path.
  • defaults – an optional dictionary of default values
from_envvar (variable_namesilent=False)

Loads a configuration from an environment variable pointing to a configuration file. This is basically just a shortcut with nicer error messages for this line of code:

app.config.from_pyfile(os.environ['YOURAPPLICATION_SETTINGS']) 
Parameters:
  • variable_name – name of the environment variable
  • silent – set to True if you want silent failure for missing files.
Returns:

bool. True if able to load config, False otherwise.

from_object (obj)

Updates the values from the given object. An object can be of one of the following two types:

  • a string: in this case the object with that name will be imported
  • an actual object reference: that object is used directly

Objects are usually either modules or classes.

Just the uppercase variables in that object are stored in the config. Example usage:

app.config.from_object('yourapplication.default_config') from yourapplication import default_config app.config.from_object(default_config) 

You should not use this function to load the actual configuration but rather configuration defaults. The actual config should be loaded withfrom_pyfile() and ideally from a location not within the package because the package might be installed system wide.

Parameters: obj – an import name or object
from_pyfile (filenamesilent=False)

Updates the values in the config from a Python file. This function behaves as if the file was imported as module with the from_object() function.

Parameters:
  • filename – the filename of the config. This can either be an absolute filename or a filename relative to the root path.
  • silent – set to True if you want silent failure for missing files.

New in version 0.7: silent parameter.

Extensions

flask. ext

This module acts as redirect import module to Flask extensions. It was added in 0.8 as the canonical way to import Flask extensions and makes it possible for us to have more flexibility in how we distribute extensions.

If you want to use an extension named 「Flask-Foo」 you would import it from ext as follows:

from flask.ext import foo 

New in version 0.8.

Stream Helpers

flask. stream_with_context (generator_or_function)

Request contexts disappear when the response is started on the server. This is done for efficiency reasons and to make it less likely to encounter memory leaks with badly written WSGI middlewares. The downside is that if you are using streamed responses, the generator cannot access request bound information any more.

This function however can help you keep the context around for longer:

from flask import stream_with_context, request, Response @app.route('/stream') def streamed_response(): @stream_with_context def generate(): yield 'Hello ' yield request.args['name'] yield '!' return Response(generate()) 

Alternatively it can also be used around a specific generator:

from flask import stream_with_context, request, Response @app.route('/stream') def streamed_response(): def generate(): yield 'Hello ' yield request.args['name'] yield '!' return Response(stream_with_context(generate())) 

New in version 0.9.

Useful Internals

class  flask.ctx. RequestContext (appenvironrequest=None)

The request context contains all request relevant information. It is created at the beginning of the request and pushed to the _request_ctx_stack and removed at the end of it. It will create the URL adapter and request object for the WSGI environment provided.

Do not attempt to use this class directly, instead use test_request_context()and request_context() to create this object.

When the request context is popped, it will evaluate all the functions registered on the application for teardown execution (teardown_request()).

The request context is automatically popped at the end of the request for you. In debug mode the request context is kept around if exceptions happen so that interactive debuggers have a chance to introspect the data. With 0.4 this can also be forced for requests that did not fail and outside of DEBUG mode. By setting'flask._preserve_context' to True on the WSGI environment the context will not pop itself at the end of the request. This is used by the test_client() for example to implement the deferred cleanup functionality.

You might find this helpful for unittests where you need the information from the context local around for a little longer. Make sure to properly pop() the stack yourself in that situation, otherwise your unittests will leak memory.

copy ()

Creates a copy of this request context with the same request object. This can be used to move a request context to a different greenlet. Because the actual request object is the same this cannot be used to move a request context to a different thread unless access to the request object is locked.

New in version 0.10.

match_request ()

Can be overridden by a subclass to hook into the matching of the request.

pop (exc=None)

Pops the request context and unbinds it by doing that. This will also trigger the execution of functions registered by the teardown_request() decorator.

Changed in version 0.9: Added the exc argument.

push ()

Binds the request context to the current context.

flask. _request_ctx_stack

The internal LocalStack that is used to implement all the context local objects used in Flask. This is a documented instance and can be used by extensions and application code but the use is discouraged in general.

The following attributes are always present on each layer of the stack:

app
the active Flask application.
url_adapter
the URL adapter that was used to match the request.
request
the current request object.
session
the active session object.
g
an object with all the attributes of the  flask.g object.
flashes
an internal cache for the flashed messages.

Example usage:

from flask import _request_ctx_stack def get_session(): ctx = _request_ctx_stack.top if ctx is not None: return ctx.session 
class  flask.ctx. AppContext (app)

The application context binds an application object implicitly to the current thread or greenlet, similar to how the RequestContext binds request information. The application context is also implicitly created if a request context is created but the application is not on top of the individual application context.

pop (exc=None)

Pops the app context.

push ()

Binds the app context to the current context.

flask. _app_ctx_stack

Works similar to the request context but only binds the application. This is mainly there for extensions to store data.

New in version 0.9.

class  flask.blueprints. BlueprintSetupState (blueprintappoptions,first_registration)

Temporary holder object for registering a blueprint with the application. An instance of this class is created by the make_setup_state() m

相關文章
相關標籤/搜索