Flask基礎(chǔ)介紹
Flask 是一個(gè)輕量級(jí)的 Web 應(yīng)用框架, 使用 Python 編寫载庭∪诩恚基于 WerkzeugWSGI 工具箱和 Jinja2 模板引擎。使用 BSD 授權(quán)敦姻。Flask 也被稱為 microframework 耕蝉,因?yàn)樗褂煤?jiǎn)單的核心宛徊,用 extension 增加其它功能佛嬉。Flask 沒(méi)有默認(rèn)使用的數(shù)據(jù)庫(kù)、窗體驗(yàn)證工具闸天。然而暖呕,F(xiàn)lask 保留了擴(kuò)增的彈性,可以用 Flask-extension 加入這些功能:ORM苞氮、窗體驗(yàn)證工具湾揽、文件上傳、各種開(kāi)放式身份驗(yàn)證技術(shù)。
Flask搭建博客步驟
博客功能
根據(jù)配置文件中的認(rèn)證允許用戶登錄以及注銷库物。僅僅支持一個(gè)用戶霸旗。
當(dāng)用戶登錄后,他們可以添加新的條目戚揭,這些條目是由純文本的標(biāo)題和 HTML 的正文構(gòu)成诱告。因?yàn)槲覀冃湃斡脩暨@里的 HTML 是安全的。
頁(yè)面倒序顯示所有條目(新的條目在前)民晒,并且用戶登入后可以在此添加新條目精居。
步驟
1.創(chuàng)建文件夾
在開(kāi)始之前,讓我們?yōu)檫@個(gè)應(yīng)用創(chuàng)建需要的文件夾:
/flaskr
?? /static
?? /templates
其中:
flaskr:項(xiàng)目總文件夾潜必。其中會(huì)存放數(shù)據(jù)庫(kù)代碼文件 schema.sql 和 主代碼文件 flaskr.py靴姿。
static:項(xiàng)目的資源文件。用于存放 css 和 javascript 文件磁滚。
templates:項(xiàng)目前端頁(yè)面文件佛吓。用于存放前端頁(yè)面模板。
2.數(shù)據(jù)庫(kù)模式
首先我們要?jiǎng)?chuàng)建數(shù)據(jù)庫(kù)模式恨旱。對(duì)于這個(gè)應(yīng)用僅一張表就足夠了辈毯,而且我們只想支持 SQLite 坝疼,所以很簡(jiǎn)單搜贤。在 Code/flaskr 目錄下新建 schema.sql 文件并向其中寫入如下代碼:
droptableif exists entries;
createtableentries (
idintegerprimary key autoincrement,
title stringnotnull,
textstringnotnull
);
這個(gè)模式由一個(gè)稱為 entries 的單表構(gòu)成,在這個(gè)表中每行包含一個(gè) id 钝凶,一個(gè) title 和一個(gè) text仪芒。id 是一個(gè)自增的整數(shù)而且是主鍵,其余兩個(gè)為非空的字符串耕陷。
3.應(yīng)用設(shè)置代碼
現(xiàn)在我們已經(jīng)有了數(shù)據(jù)庫(kù)模式了掂名,可以創(chuàng)建應(yīng)用的模塊了。在 Code/flaskr 目錄下新建 flaskr.py 文件哟沫。對(duì)于小應(yīng)用饺蔑,直接把配置放在主模塊里,正如我們現(xiàn)在要做的一樣嗜诀,這是可行的猾警。然而一個(gè)更干凈的解決方案就是單獨(dú)創(chuàng)建.ini或者.py文件接著加載或者導(dǎo)入里面的值。在 flaskr.py 文件中寫入如下代碼:
# 導(dǎo)入所有的模塊
importsqlite3
fromflaskimportFlask,request,session,g,redirect,url_for,abort,render_template,flash
# 配置文件
DATABASE='/tmp/flaskr.db'
ENV='development'
DEBUG=True
SECRET_KEY='development key'
USERNAME='admin'
PASSWORD='default'
下一步我們能夠創(chuàng)建真正的應(yīng)用隆敢,修改 flaskr.py 文件在其中配置初始化:
# 創(chuàng)建應(yīng)用
app=Flask(__name__)
app.config.from_object(__name__)
from_object() 將會(huì)尋找給定的對(duì)象(如果它是一個(gè)字符串发皿,則會(huì)導(dǎo)入它),搜尋里面定義的全部大寫的變量拂蝎。在我們的這種情況中穴墅,配置文件就是我們上面寫的幾行代碼。 你也可以將他們分別存儲(chǔ)到多個(gè)文件。通常從配置文件中加載配置是一個(gè)好的主意玄货。這時(shí)可以使用 from_envvar() 來(lái)實(shí)現(xiàn)皇钞,可以用它替換上面的 from_object():
app.config.from_envvar('FLASKR_SETTINGS',silent=True)
這種方法我們可以設(shè)置一個(gè)名為 FLASKR_SETTINGS 的環(huán)境變量來(lái)設(shè)定一個(gè)配置文件載入后是否覆蓋默認(rèn)值。靜默開(kāi)關(guān) silent=True 告訴 Flask 不去關(guān)心這個(gè)環(huán)境變量鍵值是否存在誉结。SECRET_KEY 是為了保持客戶端的會(huì)話安全鹅士。明智地選擇該鍵,使得它難以猜測(cè)惩坑,最好是盡可能復(fù)雜掉盅。
DEBUG 調(diào)試標(biāo)志啟用或禁用交互式調(diào)試。決不讓調(diào)試模式在生產(chǎn)系統(tǒng)中啟動(dòng)以舒,因?yàn)樗鼘⒃试S用戶在服務(wù)器上執(zhí)行代碼趾痘!
我們還添加了一個(gè)輕松地連接到指定數(shù)據(jù)庫(kù)的方法,這個(gè)方法用于在請(qǐng)求時(shí)打開(kāi)一個(gè)連接蔓钟,并且在交互式 Python shell 和腳本中也能使用永票,這將方便后面的使用。
在 Code/flaskr.py 文件中繼續(xù)添加如下代碼:
defconnect_db():
returnsqlite3.connect(app.config['DATABASE'])
最后如果我們想要把這個(gè)文件當(dāng)做獨(dú)立應(yīng)用來(lái)運(yùn)行滥沫,只需在服務(wù)器啟動(dòng)文件也就是 Code/flaskr.py 文件的末尾添加這一行:
if__name__=='__main__':
app.run()
4.創(chuàng)建數(shù)據(jù)庫(kù)
如前面所述侣集,F(xiàn)laskr 是一個(gè)數(shù)據(jù)庫(kù)驅(qū)動(dòng)的應(yīng)用程序,準(zhǔn)確地來(lái)說(shuō)兰绣,F(xiàn)laskr 是一個(gè)使用關(guān)系數(shù)據(jù)庫(kù)系統(tǒng)的應(yīng)用程序世分。這樣的系統(tǒng)需要一個(gè)模式告訴它們?nèi)绾未鎯?chǔ)信息。因此在首次啟動(dòng)服務(wù)器之前缀辩,創(chuàng)建數(shù)據(jù)庫(kù)模式是很重要的臭埋。
在 Code/flaskr.py 文件中添加如下代碼:
fromcontextlibimportclosing
接著我們可以創(chuàng)建一個(gè)稱為 init_db 函數(shù),該函數(shù)用來(lái)初始化數(shù)據(jù)庫(kù)臀玄。為此我們可以使用之前定義的 connect_db 函數(shù)瓢阴。 只要在 Code/flaskr.py 文件中的 connect_db 函數(shù)下添加這樣的函數(shù):
def init_db():
? ? with closing(connect_db()) as db:
? ? ? ? with app.open_resource('schema.sql') as f:
? ? ? ? ? ? db.cursor().executescript(f.read().decode())
? ? ? ? db.commit()
closing()函數(shù)允許我們?cè)?with 塊中保持?jǐn)?shù)據(jù)庫(kù)連接可用。應(yīng)用對(duì)象的 open_resource() 方法在其方框外也支持這個(gè)功能健无,因此可以在 with 塊中直接使用荣恐。這個(gè)函數(shù)從當(dāng)前位置(你的flaskr 文件夾)中打開(kāi)一個(gè)文件,并且允許你讀取它累贤。我們?cè)谶@里用它在數(shù)據(jù)庫(kù)連接上執(zhí)行一個(gè)腳本叠穆。
當(dāng)我們連接到數(shù)據(jù)庫(kù)時(shí)會(huì)得到一個(gè)數(shù)據(jù)庫(kù)連接對(duì)象(這里命名它為 db),這個(gè)對(duì)象提供給我們一個(gè)數(shù)據(jù)庫(kù)指針畦浓。指針上有一個(gè)可以執(zhí)行完整腳本的方法痹束。最后我們不顯式地提交更改, SQLite 3 或者其它事務(wù)數(shù)據(jù)庫(kù)不會(huì)這么做讶请。
現(xiàn)在可以在 Python shell 里創(chuàng)建數(shù)據(jù)庫(kù)祷嘶,導(dǎo)入并調(diào)用剛才的函數(shù):
python3
>>> from flaskr import init_db
>>> init_db()
5.請(qǐng)求數(shù)據(jù)庫(kù)連接
所有我們的函數(shù)中都需要數(shù)據(jù)庫(kù)連接屎媳,因此在請(qǐng)求之前初始化它們,在請(qǐng)求結(jié)束后自動(dòng)關(guān)閉它們就很有意義论巍。
Flask 允許我們使用 before_request() 烛谊,after_request() 和 teardown_request() 裝飾器來(lái)實(shí)現(xiàn)這個(gè)功能,在 Code/flaskr.py 文件中添加如下代碼:
@app.before_request
def before_request():
? ? g.db = connect_db()
@app.teardown_request
def teardown_request(exception):
? ? g.db.close()
使用 before_request() 裝飾器的函數(shù)會(huì)在請(qǐng)求之前被調(diào)用而且不帶參數(shù)嘉汰。使用 after_request() 裝飾器的函數(shù)會(huì)在請(qǐng)求之后被調(diào)用且傳入將要發(fā)給客戶端的響應(yīng)丹禀。
它們必須返回那個(gè)響應(yīng)對(duì)象或是不同的響應(yīng)對(duì)象。但當(dāng)異常拋出時(shí)鞋怀,它們不一定會(huì)被執(zhí)行双泪,這時(shí)可以使用 teardown_request() 裝飾器。它裝飾的函數(shù)將在響應(yīng)構(gòu)造后執(zhí)行密似,并不允許修改請(qǐng)求焙矛,返回的值會(huì)被忽略。如果在請(qǐng)求已經(jīng)被處理的時(shí)候拋出異常残腌,它會(huì)被傳遞到每個(gè)函數(shù)村斟,否則會(huì)傳入一個(gè) None。
我們把當(dāng)前的數(shù)據(jù)庫(kù)連接保存在 Flask 提供的 g 特殊對(duì)象中抛猫。這個(gè)對(duì)象只能保存一次請(qǐng)求的信息蟆盹,并且在每個(gè)函數(shù)里都可用。不要用其它對(duì)象來(lái)保存信息闺金,因?yàn)樵诙嗑€程環(huán)境下將不可行逾滥。特殊的對(duì)象 g 在后臺(tái)有一些神奇的機(jī)制來(lái)保證它在做正確的事情。
6.建立視圖
現(xiàn)在數(shù)據(jù)庫(kù)連接已經(jīng)正常工作掖看,可以開(kāi)始編寫視圖函數(shù)匣距。我們需要四個(gè)視圖函數(shù):
顯示條目
這個(gè)視圖顯示所有存儲(chǔ)在數(shù)據(jù)庫(kù)中的條目面哥。它監(jiān)聽(tīng)著應(yīng)用的根地址以及將會(huì)從數(shù)據(jù)庫(kù)中查詢標(biāo)題和內(nèi)容哎壳。 id 值最大的條目(最新的條目)將在最前面。從游標(biāo)返回的行是按 select 語(yǔ)句中聲明的列組織的元組尚卫。對(duì)于像我們這樣的小應(yīng)用是足夠的归榕,但是你可能要把它們轉(zhuǎn)換成字典,如果你對(duì)如何轉(zhuǎn)換成字典感興趣的話吱涉,請(qǐng)查閱簡(jiǎn)化查詢例子刹泄。
視圖函數(shù)將會(huì)把條目作為字典傳入 show_entries.html 模板并返回渲染結(jié)果。
在 flaskr/flaskr.py 文件中添加如下代碼:
@app.route('/')
def show_entries():
? ? cur = g.db.execute('select title, text from entries order by id desc')? # 查詢語(yǔ)句
? ? entries = [dict(title=row[0], text=row[1]) for row in cur.fetchall()]? # 將查詢結(jié)果轉(zhuǎn)換為字典
? ? return render_template('show_entries.html', entries=entries)
添加新條目
這個(gè)視圖允許登錄的用戶添加新的條目怎爵。它只回應(yīng) POST 請(qǐng)求特石,實(shí)際的表單是顯示在 show_entries 頁(yè)面。如果正常工作的話鳖链,我們用 flash() 向下一個(gè)請(qǐng)求閃現(xiàn)一條信息并且跳轉(zhuǎn)回 show_entries 頁(yè)面姆蘸。
在 flaskr/flaskr.py 文件中添加如下代碼:
@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']])? # 向數(shù)據(jù)庫(kù)中插入數(shù)據(jù)
? ? g.db.commit()? # 更新數(shù)據(jù)
? ? flash('New entry was successfully posted')? # 閃現(xiàn)一條消息
? ? return redirect(url_for('show_entries'))
注意我們這里檢查用戶登錄情況( logged_in 鍵存在會(huì)話中,并且為 True )。向 SQL 語(yǔ)句中傳遞參數(shù)逞敷,需要在語(yǔ)句中使用問(wèn)號(hào) ? 來(lái)代替參數(shù)狂秦,并把參數(shù)放在列表中進(jìn)行傳遞。不要使用字符串格式化的方式直接把參數(shù)傳入 SQL 語(yǔ)句中推捐,這樣可能會(huì)有潛在的 SQL 注入風(fēng)險(xiǎn)裂问。
登錄
這些函數(shù)是用于用戶登錄以及注銷。登錄時(shí)依據(jù)在配置中的值檢查用戶名和密碼并且在會(huì)話中設(shè)置 logged_in 鍵值牛柒。如果用戶成功登錄堪簿,logged_in 鍵值被設(shè)置成 True ,并跳轉(zhuǎn)回 show_entries 頁(yè)面皮壁。此外戴甩,會(huì)有消息閃現(xiàn)來(lái)提示用戶登錄成功。
如果發(fā)生錯(cuò)誤闪彼,模板會(huì)通知甜孤,并提示重新登錄。在 flaskr/flaskr.py 文件中添加如下代碼:
@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? # 成功登錄畏腕,在 session 中添加一個(gè) logged_in 值為 True
? ? ? ? ? ? flash('You were logged in')? # 閃現(xiàn)一條消息
? ? ? ? ? ? return redirect(url_for('show_entries'))? # 重定向到首頁(yè)
? ? return render_template('login.html', error=error)? # 如果沒(méi)有成功登錄則返回登錄頁(yè)面以及錯(cuò)誤信息
注銷
另一方面缴川,注銷函數(shù)從會(huì)話中移除了 logged_in 鍵值。這里我們使用一個(gè)大絕招:如果你使用字典的 pop() 方法并傳入第二個(gè)參數(shù)(默認(rèn))描馅,這個(gè)方法會(huì)從字典中刪除這個(gè)鍵把夸,如果這個(gè)鍵不存在則什么都不做。這很有用铭污,因?yàn)槲覀儾恍枰獧z查用戶是否已經(jīng)登入恋日。
在 flaskr/flaskr.py 文件中添加如下代碼:
@app.route('/logout')
def logout():
? ? session.pop('logged_in', None)? # 移除 logged_in 鍵
? ? flash('You were logged out')? # 閃現(xiàn)消息
? ? return redirect(url_for('show_entries'))? # 重定向到首頁(yè)
7.模板
現(xiàn)在我們應(yīng)該開(kāi)始編寫模板。
layout.html
這個(gè)模板包含 HTML 主體結(jié)構(gòu)嘹狞,標(biāo)題和一個(gè)登錄鏈接(或者當(dāng)用戶已登錄則提供注銷)岂膳。如果有閃現(xiàn)信息的話它也將顯示閃現(xiàn)信息。{% block body %} 塊能夠被子模板中的同樣名字( body )的塊替代磅网。
session 字典在模板中同樣可用的谈截,你能用它檢查用戶是否登錄。注意在 Jinja 中你可以訪問(wèn)不存在的對(duì)象/字典屬性或成員涧偷,如同下面的代碼簸喂,即便 logged_in 鍵不存在,仍然可以正常工作燎潮。
在 flaskr/templates 目錄下新建 layout.html 文件并寫入如下代碼:
<!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è)模板繼承了上面的layout.html模板用來(lái)顯示信息喻鳄。注意for遍歷了所有我們用render_template()函數(shù)傳入的信息。我們同樣告訴表單提交到add_entry函數(shù)通過(guò)使用 HTTP 的POST方法确封。
在 flaskr/templates 目錄下新建 show_entries.html 文件并寫入如下代碼:
{% 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
最后是登錄模板除呵,基本上只顯示一個(gè)允許用戶登錄的表單唉锌。
在 flaskr/templates 目錄下新建 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 %}
添加樣式
現(xiàn)在其它一切都正常工作,是時(shí)候給應(yīng)用添加些樣式竿奏。
在 Code/flaskr/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; }
8.運(yùn)行應(yīng)用
這樣我們就完成了簡(jiǎn)單博客應(yīng)用的代碼編寫
$ python3 flaskr.py
* Serving Flask app "flaskr" (lazy loading)
* Environment: development
* Debug mode: on
* Running on http://127.0.0.1:5000/ (Press CTRL+C to quit)
* Restarting with stat
* Debugger is active!
* Debugger PIN: 183-564-157
訪問(wèn)首頁(yè) http://127.0.0.1:5000 袄简,即可開(kāi)啟博客