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

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

教程

想要用 Python 和 Flask 開(kāi)發(fā)應(yīng)用嗎?讓我們來(lái)邊看例子邊學(xué)習(xí)。本教程中我們將會(huì)創(chuàng)建 一個(gè)微博應(yīng)用。這個(gè)應(yīng)用只支持單一用戶,只能創(chuàng)建文本條目,也沒(méi)有不能訂閱和評(píng)論, 但是已經(jīng)具備一個(gè)初學(xué)者需要掌握的功能。這個(gè)應(yīng)用只需要使用 Flask 和 SQLite , SQLite 是 Python 自帶的。

如果你想要事先下載完整的源代碼或者用于比較,請(qǐng)查看示例源代碼 。

Flaskr 介紹

我們把教程中的博客應(yīng)用稱為 flaskr ,當(dāng)然你也可以隨便取一個(gè)沒(méi)有 Web-2.0 氣息的名字 ;) 它的基本功能如下:

  1. 讓用戶可以根據(jù)配置文件中的信息登錄和注銷。只支持一個(gè)用戶。
  2. 用戶登錄以后可以添加新的博客條目。條目由文本標(biāo)題和支持 HTML 代碼的內(nèi)容組成。 因?yàn)槲覀冃湃斡脩?,所以不?duì)內(nèi)容中的 HTML 進(jìn)行凈化處理。
  3. 頁(yè)面以倒序(新的在上面)顯示所有條目。并且用戶登錄以后可以在這個(gè)頁(yè)面添加新的條目。

我們直接在應(yīng)用中使用 SQLite3 ,因?yàn)樵谶@種規(guī)模的應(yīng)用中 SQlite3 已經(jīng)夠用了。如果 是大型應(yīng)用,那么就有必要使用能夠好的處理數(shù)據(jù)庫(kù)連接的 SQLAlchemy 了,它可以 同時(shí)對(duì)應(yīng)多種數(shù)據(jù)庫(kù),并做其他更多的事情。如果你的數(shù)據(jù)更適合使用 NoSQL 數(shù)據(jù)庫(kù), 那么也可以考慮使用某種流行的 NoSQL 數(shù)據(jù)庫(kù)。

這是教程應(yīng)用完工后的截圖:

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

步驟 0 :創(chuàng)建文件夾

在開(kāi)始之前需要為應(yīng)用創(chuàng)建下列文件夾:

/flaskr
    /static
    /templates

flaskr 文件夾不是一個(gè) Python 包,只是一個(gè)用來(lái)存放我們文件的地方。我們將把以后要用到的數(shù)據(jù)庫(kù)模式和主模塊放在這個(gè)文件夾中。 static 文件夾中的文件是用于供應(yīng)用用戶通過(guò) HTTP 訪問(wèn)的文件,主要是 CSS 和 javascript 文件。 Flask 將會(huì)在 templates 文件夾中搜索 Jinja2 模板,所有在教程中的模板都放在 templates 文件夾中。

步驟 1 :數(shù)據(jù)庫(kù)模式

首先我們要?jiǎng)?chuàng)建數(shù)據(jù)庫(kù)模式。本應(yīng)用只需要使用一張表,并且由于我們使用 SQLite , 所以這一步非常簡(jiǎn)單。把以下內(nèi)容保存為 schema.sql 文件并放在我們上一步創(chuàng)建的 flaskr 文件夾中就行了:

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

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

步驟 2 :應(yīng)用構(gòu)建代碼

現(xiàn)在我們已經(jīng)準(zhǔn)備好了數(shù)據(jù)庫(kù)模式了,下面來(lái)創(chuàng)建應(yīng)用模塊。我們把模塊命名為 flaskr.py ,并放在 flaskr 文件夾中。為了方便初學(xué)者學(xué)習(xí),我們把庫(kù)的導(dǎo)入與相關(guān)配置放在了一起。對(duì)于小型應(yīng)用來(lái)說(shuō),可以把配置直接放在模塊中。但是更加清晰的 方案是把配置放在一個(gè)獨(dú)立的 .ini 或 .py 文件中,并在模塊中導(dǎo)入配置的值。

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

接著創(chuàng)建真正的應(yīng)用,并用同一文件中的配置來(lái)初始化,在 flaskr.py 文件中:

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

from_object() 會(huì)查看給定的對(duì)象(如果該對(duì)象是一個(gè)字符串就會(huì)直接導(dǎo)入它),搜索對(duì)象中所有變量名均為大字字母的變量。在我們的應(yīng)用中,已經(jīng)將配 置寫(xiě)在前面了。你可以把這些配置放到一個(gè)獨(dú)立的文件中。

通常,從一個(gè)配置文件中導(dǎo)入配置是比較好的做法,我們使用 from_envvar() 來(lái)完成這個(gè)工作,把上面的 from_object() 一行替換為:

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

這樣做就可以設(shè)置一個(gè) FLASKR_SETTINGS 的環(huán)境變量來(lái)指定一個(gè)配置文件,并根據(jù)該文件來(lái)重載缺省的配置。 silent 開(kāi)關(guān)的作用是告訴 Flask 如果沒(méi)有這個(gè)環(huán)境變量 不要報(bào)錯(cuò)。

secret_key (密鑰)用于保持客戶端會(huì)話安全,請(qǐng)謹(jǐn)慎地選擇密鑰,并盡可能的使 復(fù)雜而且不容易被猜到。 DEBUG 標(biāo)志用于開(kāi)關(guān)交互調(diào)試器。因?yàn)檎{(diào)試模式允許用戶執(zhí)行服務(wù)器上的代碼,所以永遠(yuǎn)不要在生產(chǎn)環(huán)境中打開(kāi)調(diào)試模式 !

我們還添加了一個(gè)方便連接指定數(shù)據(jù)庫(kù)的方法。這個(gè)方法可以用于在請(qǐng)求時(shí)打開(kāi)連接,也可以用于 Python 交互終端或代碼中。以后會(huì)派上用場(chǎng)。

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

最后,在文件末尾添加以單機(jī)方式啟動(dòng)服務(wù)器的代碼:

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

到此為止,我們可以順利運(yùn)行應(yīng)用了。輸入以下命令開(kāi)始運(yùn)行:

python flaskr.py

你會(huì)看到服務(wù)器已經(jīng)運(yùn)行的信息,其中包含應(yīng)用訪問(wèn)地址。

因?yàn)槲覀冞€沒(méi)創(chuàng)建視圖,所以當(dāng)你在瀏覽器中訪問(wèn)這個(gè)地址時(shí),會(huì)得到一個(gè) 404 頁(yè)面未 找到錯(cuò)誤。很快我們就會(huì)談到視圖,但我們先要弄好數(shù)據(jù)庫(kù)。

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

想讓你的服務(wù)器被公開(kāi)訪問(wèn)?詳見(jiàn)外部可見(jiàn)的服務(wù)器 。

步驟 3 :創(chuàng)建數(shù)據(jù)庫(kù)

如前所述 Flaskr 是一個(gè)數(shù)據(jù)庫(kù)驅(qū)動(dòng)的應(yīng)用,更準(zhǔn)確地說(shuō)是一個(gè)關(guān)系型數(shù)據(jù)庫(kù)驅(qū)動(dòng)的 應(yīng)用。關(guān)系型數(shù)據(jù)庫(kù)需要一個(gè)數(shù)據(jù)庫(kù)模式來(lái)定義如何儲(chǔ)存信息,因此必須在第一次運(yùn)行 服務(wù)器前創(chuàng)建數(shù)據(jù)庫(kù)模式。

使用 sqlite3 命令通過(guò)管道導(dǎo)入 schema.sql 創(chuàng)建模式:

sqlite3 /tmp/flaskr.db < schema.sql

上述方法的不足之處是需要額外的 sqlite3 命令,但這個(gè)命令不是每個(gè)系統(tǒng)都有的。而且還必須提供數(shù)據(jù)庫(kù)的路徑,容易出錯(cuò)。因此更好的方法是在應(yīng)用中添加一個(gè)數(shù)據(jù)庫(kù)初始化函數(shù)。

添加的方法是:首先從 contextlib 庫(kù)中導(dǎo)入 contextlib.closing() 函數(shù),即在 flaskr.py 文件的導(dǎo)入部分添加如下內(nèi)容:

from contextlib import closing

接下來(lái),可以創(chuàng)建一個(gè)用來(lái)初始化數(shù)據(jù)庫(kù)的 init_db 函數(shù),其中我們使用了先前創(chuàng)建的 connect_db 函數(shù)。把這個(gè)初始化函數(shù)放在 flaskr.py 文件中的connect_db 函數(shù) 下面:

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() 幫助函數(shù)允許我們?cè)?with 代碼塊保持?jǐn)?shù)據(jù)庫(kù)連接打開(kāi)。應(yīng)用對(duì)象的 open_resource() 方法支持也支持這個(gè)功能, 可以在 with 代碼塊中直接使用。這個(gè)函數(shù)打開(kāi)一個(gè)位于來(lái)源位置(你的 flaskr 文件夾)的文件并允許你讀取文件的內(nèi)容。這里我們用于在數(shù)據(jù)庫(kù)連接上執(zhí)行代碼。

當(dāng)我們連接到數(shù)據(jù)庫(kù)時(shí),我們得到一個(gè)提供指針的連接對(duì)象(本例中的 db )。這個(gè) 指針有一個(gè)方法可以執(zhí)行完整的代碼。最后我們提供要做的修改。 SQLite 3 和其他事務(wù)型數(shù)據(jù)庫(kù)只有在顯式提交時(shí)才會(huì)真正提交。

現(xiàn)在可以創(chuàng)建數(shù)據(jù)庫(kù)了。打開(kāi) Python shell ,導(dǎo)入,調(diào)用函數(shù):

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

故障處理

如果出現(xiàn)表無(wú)法找到的問(wèn)題,請(qǐng)檢查是否寫(xiě)錯(cuò)了函數(shù)名稱(應(yīng)該是 init_db ), 是否寫(xiě)錯(cuò)了表名(例如單數(shù)復(fù)數(shù)錯(cuò)誤)。

步驟 4 :請(qǐng)求數(shù)據(jù)庫(kù)連接

現(xiàn)在我們已經(jīng)學(xué)會(huì)如何打開(kāi)并在代碼中使用數(shù)據(jù)庫(kù)連接,但是如何優(yōu)雅地在請(qǐng)求時(shí)使用它呢?我們會(huì)在每一個(gè)函數(shù)中用到數(shù)據(jù)庫(kù)連接,因此有必要在請(qǐng)求之前初始化連接,并在請(qǐng)求之后關(guān)閉連接。

Flask 中可以使用 before_request() 、 after_request()teardown_request() 裝飾器達(dá)到這個(gè)目的:

@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()
    g.db.close()

使用 before_request() 裝飾的函數(shù)會(huì)在請(qǐng)求之前調(diào)用,且不傳遞 參數(shù)。使用 after_request() 裝飾的函數(shù)會(huì)在請(qǐng)求之后調(diào)用,且 傳遞發(fā)送給客戶端響應(yīng)對(duì)象。它們必須傳遞響應(yīng)對(duì)象,所以在出錯(cuò)的情況下就不會(huì)執(zhí)行。 因此我們就要用到 teardown_request() 裝飾器了。這個(gè)裝飾器下 的函數(shù)在響應(yīng)對(duì)象構(gòu)建后被調(diào)用。它們不允許修改請(qǐng)求,并且它們的返回值被忽略。如果 請(qǐng)求過(guò)程中出錯(cuò),那么這個(gè)錯(cuò)誤會(huì)傳遞給每個(gè)函數(shù);否則傳遞 None 。

我們把數(shù)據(jù)庫(kù)連接保存在 Flask 提供的特殊的 g 對(duì)象中。這個(gè)對(duì)象與 每一個(gè)請(qǐng)求是一一對(duì)應(yīng)的,并且只在函數(shù)內(nèi)部有效。不要在其它對(duì)象中儲(chǔ)存類似信息, 因?yàn)樵诙嗑€程環(huán)境下無(wú)效。這個(gè)特殊的 g 對(duì)象會(huì)在后臺(tái)神奇的工作,保證系統(tǒng)正常運(yùn)行。

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

Hint

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

如果你按教程一步一步讀下來(lái),那么可能會(huì)疑惑應(yīng)該把這個(gè)步驟和以后的代碼放在哪 里?比較有條理的做法是把這些模塊級(jí)別的函數(shù)放在一起,并把新的 before_request 和 teardown_request 函數(shù)放在前文的 init_db 函數(shù) 下面(即按照教程的順序放置)。

如果你已經(jīng)暈頭轉(zhuǎn)向了,那么你可以參考一下 示例源代碼 。在 Flask 中,你可以把應(yīng)用的所有代碼都放在同一個(gè) Python 模塊中。但是你沒(méi)有必要這樣做,尤其是當(dāng)你的應(yīng)用變大了的時(shí)候,更不應(yīng)當(dāng)這樣。

步驟 5 :視圖函數(shù)

現(xiàn)在數(shù)據(jù)庫(kù)連接弄好了,接著開(kāi)始寫(xiě)視圖函數(shù)。我們共需要四個(gè)視圖函數(shù):

顯示條目

這個(gè)視圖顯示所有數(shù)據(jù)庫(kù)中的條目。它綁定應(yīng)用的根地址,并從數(shù)據(jù)庫(kù)中讀取 title 和 text 字段。 id 最大的記錄(最新的條目)在最上面。從指針?lè)祷氐挠涗浖且粋€(gè)包含 select 語(yǔ)句查詢結(jié)果的元組。對(duì)于教程應(yīng)用這樣的小應(yīng)用,做到這樣就已經(jīng)夠好了。但是你可能想要把結(jié)果轉(zhuǎn)換為字典,具體做法參見(jiàn)簡(jiǎn)化查詢 中的例子。

這個(gè)視圖會(huì)把條目作為字典傳遞給 show_entries.html 模板,并返回渲染結(jié)果:

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

添加一個(gè)新條目

這個(gè)視圖可以讓一個(gè)登錄后的用戶添加一個(gè)新條目。本視圖只響應(yīng) POST 請(qǐng)求,真正的表單顯示在 show_entries 頁(yè)面中。如果一切順利,我們會(huì) flash() 一個(gè)消息給下一個(gè)請(qǐng)求并重定向回到 show_entries 頁(yè)面:

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

注意,我們?cè)诒疽晥D中檢查了用戶是否已經(jīng)登錄(即檢查會(huì)話中是否有 logged_in 鍵,且對(duì)應(yīng)的值是否為 True )。

安全性建議

請(qǐng)像示例代碼一樣確保在構(gòu)建 SQL 語(yǔ)句時(shí)使用問(wèn)號(hào)。否則當(dāng)你使用字符串構(gòu)建 SQL 時(shí)容易遭到 SQL 注入攻擊。更多內(nèi)容參見(jiàn) 在 Flask 中使用 SQLite 3 。

登錄和注銷

這些函數(shù)用于用戶登錄和注銷。登錄視圖根據(jù)配置中的用戶名和密碼驗(yàn)證用戶并在會(huì)話中設(shè)置 logged_in 鍵值。如果用戶通過(guò)驗(yàn)證,鍵值設(shè)為 True ,那么用戶會(huì)被重定向到 show_entries 頁(yè)面。另外閃現(xiàn)一個(gè)信息,告訴用戶已登錄成功。如果出現(xiàn)錯(cuò)誤,模板會(huì) 提示錯(cuò)誤信息,并讓用戶重新登錄:

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

登出視圖則正好相反,把鍵值從會(huì)話中刪除。在這里我們使用了一個(gè)小技巧:如果你使用字典的 pop() 方法并且傳遞了第二個(gè)參數(shù)(鍵的缺省值),那么當(dāng)字典中有 這個(gè)鍵時(shí)就會(huì)刪除這個(gè)鍵,否則什么也不做。這樣做的好處是我們不用檢查用戶是否已經(jīng)登錄了。

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

步驟 6 :模板

現(xiàn)在開(kāi)始寫(xiě)模板。如果我們現(xiàn)在訪問(wèn) URL ,那么會(huì)得到一個(gè) Flask 無(wú)法找到模板文件的 異常。 Flask 使用 Jinja2 模板語(yǔ)法并默認(rèn)開(kāi)啟自動(dòng)轉(zhuǎn)義。也就是說(shuō)除非用 Markup 標(biāo)記一個(gè)值或在模板中使用 |safe 過(guò)濾器,否則 Jinja2 會(huì)把如 < 或 > 之類的特殊字符轉(zhuǎn)義為與其 XML 等價(jià)字符。

我們還使用了模板繼承以保存所有頁(yè)面的布局統(tǒng)一。

請(qǐng)把以下模板放在 templates 文件夾中:

layout.html

這個(gè)模板包含 HTML 骨架、頭部和一個(gè)登錄鏈接(如果用戶已登錄則變?yōu)橐粋€(gè)注銷鏈接 )。如果有閃現(xiàn)信息,那么還會(huì)顯示閃現(xiàn)信息。 {% block body %} 塊會(huì)被子模板中同名( body )的塊替換。

session 字典在模板中也可以使用。你可以使用它來(lái)檢驗(yàn)用戶是否已經(jīng) 登錄。注意,在 Jinja 中可以訪問(wèn)對(duì)象或字典的不存在的屬性和成員。如例子中的 'logged_in' 鍵不存在時(shí)代碼仍然能正常運(yùn)行:

<!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

這個(gè)模板擴(kuò)展了上述的 layout.html 模板,用于顯示信息。注意, for 遍歷了我們通過(guò) render_template() 函數(shù)傳遞的所有信息。模板還告訴表單使用 POST 作為 HTTP 方法向 add_entry 函數(shù)提交數(shù)據(jù):

{% 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

最后是簡(jiǎn)單顯示用戶登錄表單的登錄模板:

{% 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 :添加樣式

現(xiàn)在萬(wàn)事俱備,只剩給應(yīng)用添加一些樣式了。只要把以下內(nèi)容保存為 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; }

額外贈(zèng)送:測(cè)試應(yīng)用

現(xiàn)在你已經(jīng)完成了整個(gè)應(yīng)用,一切都運(yùn)行正常。為了方便以后進(jìn)行完善修改,添加自動(dòng)測(cè)試不失為一個(gè)好主意。本教程中的應(yīng)用已成為測(cè)試 Flask 應(yīng)用文檔中演示如何進(jìn)行 單元測(cè)試的例子,可以去看看測(cè)試 Flask 應(yīng)用是多么容易

? Copyright 2013, Armin Ronacher. Created using Sphinx.