Flask筆記
2.1初始化
Web 服務(wù)器使用一種名為 Web 服務(wù)器網(wǎng)關(guān)接口
(Web Server Gateway Interface,WSGI)的協(xié)議侥猬,把接收自客戶端的所有請求都轉(zhuǎn)交給這個對象處理胆数。程序?qū)嵗?Flask 類的對象
2.2 路由和視圖函數(shù)
程序?qū)嵗枰缹γ總€ URL 請求運(yùn)行哪些代碼越平,所以保存了一個URL到
Python 函數(shù)的映射關(guān)系。處理 URL 和函數(shù)之間關(guān)系的程序稱為路由亩歹。
在 Python 代碼中嵌入響應(yīng)字符串會導(dǎo)致代碼難以維護(hù)
2.3啟動服務(wù)器
name=='main' 是 Python 的慣常用法书幕,在這里確保直接執(zhí)行這個腳本時才啟動開發(fā)Web 服務(wù)器新荤。如果這個腳本由其他腳本引入,程序假定父級腳本會啟動不同的服務(wù)器台汇,因此不會執(zhí)行 app.run()苛骨。
服務(wù)器啟動后,會進(jìn)入輪詢苟呐,等待并處理請求痒芝。輪詢會一直運(yùn)行,直到程序停止牵素,比如按Ctrl-C 鍵严衬。
2.5請求-響應(yīng)循環(huán)
2.5.1 程序和請求上下文
Flask 使用上下文臨時把某些對象
變?yōu)槿挚稍L問。
變量名 | 上下文 | 說明 |
---|---|---|
current_app | 程序上下文 | 當(dāng)前激活程序的程序?qū)嵗?/td> |
g | 程序上下文 | 處理請求時用作臨時存儲的對象笆呆。每次請求都會重設(shè)這個變量 |
request | 請求上下文 | 請求對象请琳,封裝了客戶端發(fā)出的 |
session | 請求上下文 | 用戶會話,用于存儲請求之間需要“記住”的值的詞典 |
2.5.2 請求調(diào)度
程序收到客戶端發(fā)來的請求時赠幕,要找到處理該請求的視圖函數(shù)俄精。為了完成這個任務(wù),F(xiàn)lask
會在程序的 URL 映射中查找請求的 URL劣坊。URL 映射是 URL 和視圖函數(shù)之間的對應(yīng)關(guān)系嘀倒。
Flask 使用 app.route 修飾器或者非修飾器形式的 app.add_url_rule() 生成映射。
2.5.3 請求鉤子
有時在處理請求之前或之后執(zhí)行代碼會很有用
- before_first_request:注冊一個函數(shù)局冰,在處理第一個請求之前運(yùn)行测蘑。
- before_request:注冊一個函數(shù),在每次請求之前運(yùn)行康二。
- after_request:注冊一個函數(shù)碳胳,如果沒有未處理的異常拋出,在每次請求之后運(yùn)行沫勿。
- teardown_request:注冊一個函數(shù)挨约,即使有未處理的異常拋出味混,也在每次請求之后運(yùn)行。
2.5.4 響應(yīng)
Flask 調(diào)用視圖函數(shù)后诫惭,會將其返回值作為響應(yīng)的內(nèi)容翁锡。大多數(shù)情況下,響應(yīng)就是一個簡
單的字符串夕土,作為 HTML 頁面回送客戶端馆衔。
但 HTTP 協(xié)議需要的不僅是作為請求響應(yīng)的字符串。HTTP 響應(yīng)中一個很重要的部分是狀
態(tài)碼怨绣,F(xiàn)lask 默認(rèn)設(shè)為 200角溃,這個代碼表明請求已經(jīng)被成功處理。
3.1 Jinja2模板引擎
3.1.1 渲染模板
Flask 提供的 render_template 函數(shù)把 Jinja2 模板引擎集成到了程序中篮撑。render_template 函
數(shù)的第一個參數(shù)是模板的文件名
3.1.2 變量
在模板中使用的 {{ name }} 結(jié)構(gòu)表示一個變量减细,它是一種特殊的占位符,告訴模
板引擎這個位置的值從渲染模板時使用的數(shù)據(jù)中獲取赢笨。
Jinja2 能識別所有類型的變量未蝌,甚至是一些復(fù)雜的類型,例如列表茧妒、字典和對象树埠。在模板
可以使用過濾器修改變量,過濾器名添加在變量名之后嘶伟,中間使用豎線分隔。例如又碌,下述
模板以首字母大寫形式顯示變量 name 的值:
Hello, {{ name|capitalize }}
3.1.3 控制結(jié)構(gòu)
下面這個例子展示了如何在模板中使用條件控制語句:
{% if user %}
Hello, {{ user }}!
{% else %}
Hello, Stranger!
{% endif %}
另一種常見需求是在模板中渲染一組元素九昧。下例展示了如何使用 for 循環(huán)實現(xiàn)這一需求:
<ul>
{% for comment in comments %}
<li>{{ comment }}</li>
{% endfor %}
</ul>
Jinja2 還支持宏。宏類似于 Python 代碼中的函數(shù)毕匀。
需要在多處重復(fù)使用的模板代碼片段可以寫入單獨的文件铸鹰,再包含在所有模板中,以避免
重復(fù):
{% include 'common.html' %}
3.2 使用Flask-Bootstrap集成Twitter Bootstrap
Bootstrap(http://getbootstrap.com/)是 Twitter 開發(fā)的一個開源框架皂岔,它提供的用戶界面組
件可用于創(chuàng)建整潔且具有吸引力的網(wǎng)頁蹋笼,而且這些網(wǎng)頁還能兼容所有現(xiàn)代 Web 瀏覽器。
3.3 自定義錯誤頁面
像常規(guī)路由一樣躁垛,F(xiàn)lask 允許程序使用基于模板的自定義錯誤頁面剖毯。最常見的錯誤代碼有
兩個:404,客戶端請求未知頁面或路由時顯示教馆;500逊谋,有未處理的異常時顯示。為這兩個
錯誤代碼指定自定義處理程序的方式如示例 3-6 所示土铺。
@app.errorhandler(404)
def page_not_found(e):
return render_template('404.html'), 404
@app.errorhandler(500)
def internal_server_error(e):
return render_template('500.html'), 500
3.4 鏈接
在模板中直接編寫簡單路由的 URL 鏈接不難胶滋,但對于包含可變部分的動態(tài)路由板鬓,在模板
中構(gòu)建正確的 URL 就很困難。而且究恤,直接編寫 URL 會對代碼中定義的路由產(chǎn)生不必要的
依賴關(guān)系俭令。如果重新定義路由,模板中的鏈接可能會失效部宿。
為了避免這些問題抄腔,F(xiàn)lask 提供了 url_for() 輔助函數(shù),它可以使用程序 URL 映射中保存的信息生成 URL窟赏。
3.5 靜態(tài)文件
默認(rèn)設(shè)置下妓柜,F(xiàn)lask 在程序根目錄中名為 static 的子目錄中尋找靜態(tài)文件。如果需要涯穷,可在static 文件夾中使用子文件夾存放文件棍掐。
3.6 使用Flask-Moment本地化日期和時間
服務(wù)器需要統(tǒng)一時間單位,這和用戶所在的地理位置無關(guān)拷况,所以一般使用協(xié)調(diào)世界時
(Coordinated Universal Time作煌,UTC).不過用戶看到 UTC 格式的時間會感到困惑,他們更希望看到當(dāng)?shù)貢r間赚瘦,而且采用當(dāng)?shù)貞T用的格式粟誓。
一個優(yōu)雅的解決方案是,把時間單位發(fā)送給 Web 瀏覽器起意,轉(zhuǎn)換成當(dāng)?shù)貢r間鹰服,然后渲染。
4.1 跨站請求偽造保護(hù)
默認(rèn)情況下揽咕,F(xiàn)lask-WTF 能保護(hù)所有表單免受跨站請求偽造(Cross-Site Request Forgery悲酷,
CSRF)的攻擊。惡意網(wǎng)站把請求發(fā)送到被攻擊者已登錄的其他網(wǎng)站時就會引發(fā) CSRF 攻擊亲善。
為了實現(xiàn) CSRF 保護(hù)设易,F(xiàn)lask-WTF 需要程序設(shè)置一個密鑰。Flask-WTF 使用這個密鑰生成
加密令牌蛹头,再用令牌驗證請求中表單數(shù)據(jù)的真?zhèn)味俜巍TO(shè)置密鑰的方法如示例 4-1 所示。
4.2 表單類
使用 Flask-WTF 時渣蜗,每個 Web 表單都由一個繼承自 Form 的類表示屠尊。這
4.3 把表單渲染成HTML
表單字段是可調(diào)用的,在模板中調(diào)用后會渲染成 HTML耕拷。假設(shè)視圖函數(shù)把一個 NameForm 實例通過參數(shù) form 傳入模板知染,在模板中可以生成一個簡單的表單,如下所示:
<form method="POST">
{{ form.hidden_tag() }}
{{ form.name.label }} {{ form.name() }}
{{ form.submit() }}
</form>
4.4 在視圖函數(shù)中處理表單
不僅要渲染表單斑胜,還要接收表單中的數(shù)據(jù)
4.5 重定向和用戶會話
程序可以把數(shù)據(jù)存儲在用戶會話中控淡,在請求之間“記住”數(shù)據(jù)嫌吠。用戶會話是一種私有存儲,存在于每個連接到服務(wù)器的客戶端中
4.6 Flash消息
請求完成后掺炭,有時需要讓用戶知道狀態(tài)發(fā)生了變化辫诅。這里可以使用確認(rèn)消息、警告或者錯誤提醒涧狮。
僅調(diào)用 flash() 函數(shù)并不能把消息顯示出來炕矮,程序使用的模板要渲染這些消息。最好在基模板中渲染Flash 消息者冤,因為這樣所有頁面都能使用這些消息肤视。Flask 把get_flashed_messages() 函數(shù)開放給模板,用來獲取并渲染消息
5.1 SQL數(shù)據(jù)庫
關(guān)系型數(shù)據(jù)庫把數(shù)據(jù)存儲在表中涉枫,表模擬程序中不同的實體邢滑。
表的列數(shù)是固定的,行數(shù)是可變的愿汰。列定義表所表示的實體的數(shù)據(jù)屬性困后。
表中有個特殊的列,稱為主鍵衬廷,其值為表中各行的唯一標(biāo)識符摇予。表中還可以有稱為外鍵的列,引用同一個表或不同表中某行的主鍵吗跋。行之間的這種聯(lián)系稱為關(guān)系侧戴,這是關(guān)系型數(shù)據(jù)庫模型的基礎(chǔ)。
5.2 NoSQL數(shù)據(jù)庫
所有不遵循上節(jié)所述的關(guān)系模型的數(shù)據(jù)庫統(tǒng)稱為 NoSQL 數(shù)據(jù)庫跌宛。
使用 NoSQL 數(shù)據(jù)庫當(dāng)然也有好處救鲤。數(shù)據(jù)重復(fù)可以提升查詢速度。列出用戶及其角色的操作很簡單秩冈,因為無需聯(lián)結(jié)
5.3 使用SQL還是NoSQL
SQL 數(shù)據(jù)庫擅于用高效且緊湊的形式存儲結(jié)構(gòu)化數(shù)據(jù)。這種數(shù)據(jù)庫需要花費(fèi)大量精力保證數(shù)據(jù)的一致性斥扛。NoSQL 數(shù)據(jù)庫放寬了對這種一致性的要求入问,從而獲得性能上的優(yōu)勢。
5.4 Python數(shù)據(jù)庫框架
Flask 并不限制你使
用何種類型的數(shù)據(jù)庫包稀颁,因此可以根據(jù)自己的喜好選擇使用 MySQL芬失、Postgres、SQLite匾灶、Redis棱烂、MongoDB 或者 CouchDB。
- 易用性
- 性能
- 可移植性
5.5 使用Flask-SQLAlchemy管理數(shù)據(jù)庫
Flask-SQLAlchemy 是一個 Flask 擴(kuò)展阶女,簡化了在 Flask 程序中使用 SQLAlchemy 的操作颊糜。
5.6 定義模型
模型這個術(shù)語表示程序使用的持久化實體哩治。
5.7 關(guān)系
關(guān)系型數(shù)據(jù)庫使用關(guān)系把不同表中的行聯(lián)系起來。
5.8 數(shù)據(jù)庫操作
5.8.1 創(chuàng)建表
方法是使用 db.create_all()
5.8.2 插入行
db.session.add(admin_role)
5.8.3 修改行
db.session.add(admin_role)
db.session.commit()
5.8.4 刪除行
db.session.delete(mod_role)
db.session.commit()
5.8.5 查詢行
Flask-SQLAlchemy 為每個模型類都提供了 query 對象
5.9 在視圖函數(shù)中操作數(shù)據(jù)庫
5.10 集成Python shell
每次啟動 shell 會話都要導(dǎo)入數(shù)據(jù)庫實例和模型衬鱼,這真是份枯燥的工作业筏。為了避免一直重復(fù)導(dǎo)入,我們可以做些配置鸟赫,讓 Flask-Script 的 shell 命令自動導(dǎo)入特定的對象蒜胖。
5.11 使用Flask-Migrate實現(xiàn)數(shù)據(jù)庫遷移
7.1 項目結(jié)構(gòu)
多文件 Flask 程序的基本結(jié)構(gòu)
|-flasky
|-app/
|-templates/
|-static/
|-main/
|-__init__.py
|-errors.py
|-forms.py
|-views.py
|-__init__.py
|-email.py
|-models.py
|-migrations/
|-tests/
|-__init__.py
|-test*.py
|-venv/
|-requirements.txt
|-config.py
|-manage.py
這種結(jié)構(gòu)有 4 個頂級文件夾:
- Flask 程序一般都保存在名為 app 的包中;
- 和之前一樣抛蚤,migrations 文件夾包含數(shù)據(jù)庫遷移腳本台谢;
- 單元測試編寫在 tests 包中;
- 和之前一樣岁经,venv 文件夾包含 Python 虛擬環(huán)境
同時還創(chuàng)建了一些新文件:
- requirements.txt 列出了所有依賴包朋沮,便于在其他電腦中重新生成相同的虛擬環(huán)境;
- requirements.txt 列出了所有依賴包蒿偎,便于在其他電腦中重新生成相同的虛擬環(huán)境朽们;
- manage.py 用于啟動程序以及其他的程序任務(wù)。
7.2 配置選項
程序經(jīng)常需要設(shè)定多個配置诉位。這方面最好的例子就是開發(fā)骑脱、測試和生產(chǎn)環(huán)境要使用不同的數(shù)據(jù)庫,這樣才不會彼此影響苍糠。
7.3 程序包
程序包用來保存程序的所有代碼叁丧、模板和靜態(tài)文件。我們可以把這個包直接稱為app
7.3.1 使用程序工廠函數(shù)
在單個文件中開發(fā)程序很方便岳瞭,但卻有個很大的缺點拥娄,因為程序在全局作用域中創(chuàng)建,所以無法動態(tài)修改配置瞳筏。運(yùn)行腳本時稚瘾,程序?qū)嵗呀?jīng)創(chuàng)建,再修改配置為時已晚姚炕。這一點對單元測試尤其重要摊欠,因為有時為了提高測試覆蓋度,必須在不同的配置環(huán)境中運(yùn)行程序柱宦。
這個問題的解決方法是延遲創(chuàng)建程序?qū)嵗┙罚褎?chuàng)建過程移到可顯式調(diào)用的工廠函數(shù)中。這種方法不僅可以給腳本留出配置程序的時間掸刊,還能夠創(chuàng)建多個程序?qū)嵗飧猓@些實例有時在測試中非常有用。
7.3.2 在藍(lán)本中實現(xiàn)程序功能
藍(lán)本和程序類似,也可以定義路由石窑。不同的
是牌芋,在藍(lán)本中定義的路由處于休眠狀態(tài),直到藍(lán)本注冊到程序上后尼斧,路由才真正成為程序的一部分姜贡。使用位于全局作用域中的藍(lán)本時,定義路由的方法幾乎和單腳本程序一樣棺棵。
7.4 啟動腳本
頂級文件夾中的 manage.py 文件用于啟動程序楼咳。
7.5 需求文件
程序中必須包含一個 requirements.txt 文件,用于記錄所有依賴包及其精確的版本號烛恤。如果要在另一臺電腦上重新生成虛擬環(huán)境母怜,這個文件的重要性就體現(xiàn)出來了,例如部署程序時使用的電腦缚柏。pip 可以使用如下命令自動生成這個文件:
(venv) $ pip freeze >requirements.txt