1 虛擬環境
書上介紹了 virtualenv,每個venv都會拷貝一份packages到項目 /venv目錄。
比較了一下conda管理環境,可能conda更勝一籌。或者用 virtualenvwrapper
.gitignore:指定哪些文件或目錄不作同步,比如 ./venv/,*.pyc,資料庫文件.sqlite3, .mysql
推薦IDE: PyCharm
導入已有的virtualenv: File -> Setting -> Project Interprater -> 選擇項目目錄下的/venv/Python
特點:
-> new Flask Project
-> jump between View funcion and Templates
-> Git
2 基本結構
初始化:
Flask類的構造函數只有一個必須指定的參數,即程序主模塊或包的名字。在大多數程序中,Python的__name__變量就是所需的值。Flask用這個參數決定程序的根目錄,以便稍後能夠找到相對於程序根目錄的資源文件位置
路由 (route)和視圖函數 (view function):
定義路由的最簡便方式,是使用程序實例提供的app.route修飾器,把修飾的函數註冊為路由
修飾器是Python語言的標準特性,可以使用不同的方式修改函數的行為。慣常用法是使用修飾器把函數註冊為事件的處理程序。
動態路由:地址中可以包含可變部分,Flask支持在路由中使用int、float和path類型。path類型也是字符串,但不把斜線視作分隔符
默認埠是5000,可以改成其它的(flask_script.Manager也有此功能)
python manage.py runserver -p 7777
# 有些埠不能用,查詢已佔用的埠:netstat -ano;netstat -aon|findstr "6000";tasklist|findstr "<PID>";taskkill /f /t /im XXX.exe
公認埠(Well Known Ports):從0到1023,緊密綁定(binding)於一些服務。通常這些埠的通訊明確表明了某種服務協議。80埠實際上總是HTTP通訊。
註冊埠(Registered Ports):從1024到49151。它們鬆散地綁定於一些服務。這些埠同樣用於許多其它目的。例如:許多系統處理動態埠從1024左右開始。
動態和/或私有埠(Dynamic and/or Private Ports):從49152到65535。理論上,不應為服務分配這些埠。實際上,機器通常從1024起分配動態埠。
請求-響應循環
Context 上下文全局變量:
current_app 程序上下文 當前激活程序的程序實例
g 程序上下文 處理請求時用作臨時存儲的對象。每次請求都會重設這個變量
request 請求上下文 請求對象,封裝了客戶端發出的HTTP請求中的內容
session 請求上下文 用戶會話,用於存儲請求之間需要「記住」的值的詞典
URL映射是URL和視圖函數之間的對應關係。Flask使用app.route修飾器或者非修飾器形式的app.add_url_rule()生成映射。
HEAD、Options、GET是請求方法,由路由進行處理。Flask為每個路由都指定了請求方法,這樣不同的請求方法發送到相同的URL上時,會使用不同的視圖函數進行處理。HEAD和OPTIONS方法由Flask自動處理
請求 Hook: 在請求鉤子函數和視圖函數之間共享數據一般使用上下文全局變量g
before_first_request • :註冊一個函數,在處理第一個請求之前運行。
before_request • :註冊一個函數,在每次請求之前運行。
after_request • :註冊一個函數,如果沒有未處理的異常拋出,在每次請求之後運行。
teardown_request • :註冊一個函數,即使有未處理的異常拋出,也在每次請求之後運行
響應(視圖函數返回)
Flask擴展
原書更正: Importing flask.ext.script is deprecated, use flask_script instead.
3 模板 template
業務邏輯和表現邏輯 要分開
按功能分(模板不需要重用時),或按Division分(大部分模板需要重用時)
Jinja2模板引擎
模板是一個包含響應文本的文件,其中包含用佔位變量{{...}}表示的動態部分,其具體值只在請求的上下文中才能知道。使用真實值替換變量,再返回最終得到的響應字符串,這一過程稱為渲染。
模板變量
Jinja2能識別所有類型的變量,甚至是一些複雜的類型,例如列表、字典和對象
可以使用過濾器修改變量。千萬別在不可信的值上使用safe過濾器,例如用戶在表單中輸入的文本
Hello, {{ name|capitalize }
完整的過濾器列表
safe 渲染值時不轉義。默認情況下,出於安全考慮,Jinja2會轉義所有變量
capitalize 把值的首字母轉換成大寫,其他字母轉換成小寫
lower 把值轉換成小寫形式
upper 把值轉換成大寫形式
title 把值中每個單詞的首字母都轉換成大寫
trim 把值的首尾空格去掉
striptags 渲染之前把值中所有的HTML標籤都刪掉
控制結構{%...%},可用來改變模板的渲染流程 if, for, macro, import, include
需要在多處重複使用的模板代碼片段可以寫入單獨的文件,再包含 {%include 'common.html' %} 在所有模板中
另一種重複使用代碼的強大方式是模板繼承,block標籤定義的元素可在衍生模板中修改
extends指令聲明這個模板衍生自base.html。在extends指令之後,基模板中的3個塊被重新定義,模板引擎會將其插入適當的位置。注意新定義的head塊,在基模板中其內容不是空的,所以使用super()獲取原來的內容(向已經有內容的塊中添加新內容)。
使用Flask-Bootstrap
Bootstrap是客戶端框架,因此不會直接涉及伺服器。伺服器需要做的只是提供引用了Bootstrap層 疊樣式表(CSS)和JavaScript文 件的HTML響 應,並在HTML、CSS和JavaScript代碼中實例化所需組件。這些操作最理想的執行場所就是模板。
Flask-Bootstrap基模板中定義的塊:doc, head, title, styles, body, navbar, content, scripts
Bootstrap官方文檔
CDN本地加速:
修改 Base.html,引用本地的css 文件,裡面元素跟Bootstrap 重名的,則會覆蓋官方裡相同元素
以上本地加速證明是有誤的!會重複下載本地和官方Bootstrap 的 css/js
解決:
/app/__init__.py
from flask_bootstrap import Bootstrap, WebCDN, ConditionalCDN, BOOTSTRAP_VERSION, JQUERY_VERSION, HTML5SHIV_VERSION, RESPONDJS_VERSION
def create_app(config_name):
另外:本地加速 moment.js
這個文件也在國外伺服器,訪問很慢,而且 size=160KB
/app/templates/base.html
需要單獨下載 https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.10.3/moment-with-locales.min.js 到本地目錄 /app/static/
自定義錯誤頁面
url_for() 連結輔助函數
使用url_for()生成動態地址時,將動態部分作為關鍵字參數傳入。例如,
url_for('user', name='john', _external=True)的返回結果是http://localhost:5000/user/john
使用Flask-Moment本地化日期和時間。
4 Web Form表單app.config字典可用來存儲框架、擴展和程序本身的配置變量。使用標準的字典句法就能把配置值添加到app.config對象中。這個對象還提供了一些方法,可以從文件或環境中導入配置值。
Form基類由Flask-WTF擴展定義,所以從flask.ext.wtf中導入。欄位和驗證函數 可以直接從WTForms包中導入。
WTForms支持的HTML標準欄位:StringField,TextAreaField,PasswordField。。。
WTForms內建的驗證函數:Email,DataRequired...
Placeholder提示:
重定向和用戶會話
刷新頁面後會再次提交表單。大多數情況下,這並不是理想的處理方式。
很多用戶都不理解瀏覽器發出的這個警告。 基於這個原因,最好別讓 Web 程序把 POST 請求作為瀏覽器發送的最後一個請求。
這個技巧稱為Post/重定向/Get模式。
使用get()獲取字典中鍵對應的值以避免未找到鍵的異常情況,因為對於不存在的鍵,get()會返回默認值None。
Flash消息
僅調用flash()函數並不能把消息顯示出來,程序使用的模板要渲染這些消息。最好在base.html 中渲染Flash消息,因為這樣所有頁面都能使用這些消息。Flask把get_flashed_messages()函數開放給模板,用來獲取並渲染消
使用SQL還是NoSQL
SQL 資料庫擅於用高效且緊湊的形式存儲結構化數據。這種資料庫需要花費大量精力保證數據的一致性。NoSQL 資料庫放寬了對這種一致性的要求,從而獲得性能上的優勢。
MySQL Q&A:
安裝MySQL in Windows 報錯:需要預先把Windows Defender 打開,或者configure mysql server時,不要勾選「Windows Firewall」
可能要先安裝:Microsoft Visual C++ Compiler for Python 2.7
本地安裝MySQLdb: pip install mysql-python
Window7 64位下安裝可能還會報 cl.exe錯
workaround: 先conda install mysql-python,再手動複製以下目錄及文件到 venv\Lib\Site-packages下:
Anaconda2\Lib\site-packages\MySQLdb
Anaconda2\Lib\site-packages\MySQL_python-1.2.5.dist-info
Anaconda2\Lib\site-packages\_mysql*
MySQL創建connection之後,還需要創建「schema」 --> 對應SQLAlchemy裡的「database」
MySQLdb 中文亂碼的處理:
conn = MySQLdb.connect(host='localhost', user='root', passwd='XXX', db='app_englishgo', charset = 'utf8')
顯示:title.encode('gbk')
接收輸入:unicode(request.form['title'])
SQLAlchemy 和MongoEngine:資料庫抽象層代碼包(ORM、ODM),你可以使用這些抽象包直接處理高等級的 Python 對象,而不用處理如表、文檔或查詢語言此類的資料庫實體。
使用Flask-SQLAlchemy管理資料庫
MySQL mysql://username:password@hostname:port/database
SQLite(Windows) sqlite:///c:/absolute/path/to/database
Relationship 關係型資料庫
SQLAlchemy 完整的命令、過濾器、查詢執行 列表參見 SQLAlchemy 文檔
SQLAlchemy engine設置編碼,防止中文亂碼:
集成Python shell
每次啟動 shell 會話都要導入資料庫實例和模型。為避免重複導入,我們可以做些配置,讓 Flask-Script 的 shell 命令自動導入特定的對象。為 shell 命令註冊一個 make_context 回調函數
新資料庫遷移 flask-migrate
由於模型中經常會新加一行或幾行column (比如用來保存帳戶的確認狀態),此時要修改 models.py,並執行一次新資料庫遷移
MySQL Workbench 自動產生EER,可以清楚地看到各個表格之間關係:一對多 Foreign_Key、Index等
1) config.py:
2) python manage.py deploy
3) MySQL Workbench: Database -> Reverse Engineer -> 選擇 connection -> database -> 一路 Next
使用Flask-Mail提供電子郵件支持
千萬不要把帳戶密令直接寫入腳本,特別是當你計劃開源自己的作品時。讓腳本從本機環境中導入敏感
信息
Windows 用戶可按照下面的方式設定環境變量:
所有的在cmd命令行下對環境變量的修改只對當前窗口有效,不是永久性的修改。也就是說當關閉此cmd命令行窗口後,將不再起作用。永久性修改環境變量的方法有兩種:一種是直接修改註冊表(overkill python script),另一種是通過我的電腦-〉屬性-〉高級,來設置系統的環境變量
異步發送電子郵件
為了避免處理請求過程中不必要的延遲,我們可以把發送電子郵件的函數移到後臺線程(Threading)中
很多 Flask 擴展都假設已經存在激活的程序上下文和請求上下文。Flask-Mail 中的 send() 函數使用 current_app ,因此必須激活程序上下文。不過,在不同線程中執行 mail.send() 函數時,程序上下文要使用 app.app_context() 人工創建。
7 大型程序的結構
項目結構
requirements.txt 文件,用於記錄所有依賴包及其精確的版本號。以便要在另一臺電腦上重新生成虛擬環境
創建:(venv) $ pip freeze > requirements.txt
恢復:(venv) $ pip install -r requirements.txt
重組後的程序和單腳本版本使用不同的資料庫,可使用如下命令創建數據表或者升級到最新修訂版本:(venv) $ python manage.py db upgrade
8 用戶認證
Flask的認證擴展
Flask-Login:管理已登錄用戶的用戶會話。
Werkzeug:計算密碼散列值並進行核對。
itsdangerous:生成並核對加密安全令牌。
創建認證藍本
對於不同的程序功能,我們要使用不同的藍本(main, auth),這是保持代碼整齊有序的好方法
因為 Flask 認為模板的路徑是相對於程序模板文件夾而言的。為避免與 main 藍本和後續添加的藍本發生模板命名衝突,可以把藍本使用的模板保存在單獨的文件夾中
使用Flask-Login認證用戶
LoginManager 對象的 session_protection 屬性可以設為 None 、 'basic' 或 'strong' ,以提供不同的安全等級防止用戶會話遭篡改。設為 'strong' 時,Flask-Login 會記錄客戶端 IP位址和瀏覽器的用戶代理信息,如果發現異動就登出用戶。
為了保護路由只讓認證用戶訪問,Flask-Login 提供了一個 login_required 修飾器
current_user 由 Flask-Login 定義,且在視圖函數和模板中自動可用
模板中加入用戶登錄後的信息和提示效果 base.html:
按照第 4 章介紹的「Post/ 重定向 /Get 模式」,Login的 POST 請求最後也做了重定向,不過目標 URL 有兩種可能。用戶訪問未授權的 URL 時會顯示登錄表單,Flask-Login 會把原地址保存在查詢字符串的next參數中,這個參數可從 request.args 字典中讀取。如果查詢字符串中沒有 next 參數,則重定向到首頁
app/auth/views.py
用戶註冊表單 app/auth/forms.py
這個表單使用 WTForms 提供的 Regexp 驗證函數,確保 username 欄位只包含字母、數字、下劃線和點號。
密碼要輸入兩次。此時要驗證兩個密碼欄位中的值是否一致,這種驗證可使用WTForms 提供的另一驗證函數實現,即 EqualTo
如果表單類中定義了以validate_ 開頭且後面跟著欄位名的方法,這個方法就和常規的驗證函數一起調用
發送確認郵件
使用itsdangerous生成確認令牌
對藍本來說, before_request 鉤子只能應用到屬於藍本的請求上。若想在藍本中使用針對程序全局請求的鉤子,必須使用before_app_request 修飾器
角色在資料庫中的表示
賦予角色
角色驗證
用戶資料頁面
資料編輯器
用戶頭像
國內 gravatar.com被牆,改用其它方法:靜態jpg頭像
目錄:c:\git\flasky\app\static\avatar\001.jpg ~ XXX.jpg
base.html:
models.py:
user.html:
_posts.html:
_commments.html:
效果:
擴展TODO:加入性別、用戶自選頭像。。。
11 Blog articles 博客文章實現功能,即允許用戶閱讀、撰寫博客文章。本章新技術:重用模板、分頁顯示長列表以及處理富文本。
生成虛擬信息用於測試,其中功能相對完善的是ForgeryPy
添加分頁導航
paginate() 方法的返回值是一個 Pagination 類對象,這個類在 Flask-SQLAlchemy 中定義。這個對象包含很多屬性, 用於在模板中生成分頁連結
使用Markdown和Flask-PageDown支持富文本文章
• PageDown: 使用JavaScript實現的客戶端Markdown到HTML的轉換程序。
• Flask-PageDown: 為Flask包裝的PageDown,把PageDown集成到Flask-WTF表單中。
• Markdown: 使用Python實現的伺服器端Markdown到HTML的轉換程序。
• Bleach: 使用Python實現的HTML清理器。
博客文章的固定連結
博客文章編輯器
再論資料庫關係
一對多關係:是最常用的關係類型,它把一個記錄和一組相關的記錄聯繫在一起。實現這種關係時,要在「多」這一側加入一個外鍵,指向「一」這一側聯接的記錄。
多對多關係: 這種問題的解決方法是添加第三張表, 這個表稱為關聯表。多對多關係可以分解成原表和關聯表之間的兩個一對多關係
若想顯示所關注用戶發布的所有文章,第一步顯然先要獲取這些用戶,然後獲取各用戶的文章,再按一定順序排列,寫入單獨列表。可是這種方式的伸縮性不好,隨著資料庫不斷變大,生成這個列表的工作量也不斷增長,而且分頁等操作也無法高效率完成。獲取博客文章的高效方式是只用一次查詢。
完成這個操作的資料庫操作稱為聯結。聯結操作用到兩個或更多的數據表, 在其中查找滿足指定條件的記錄組合, 再把記錄組合插入一個臨時表中,這個臨時表就是聯結查詢的結果。
創建函數更新資料庫這一技術經常用來更新已部署的程序,因為運行腳本更新比手動更新資料庫更少出錯。
評論屬於某篇博客文章,因此定義了一個從posts表到comments表的一對多關係。使用這個關係可以獲取某篇特定博客文章的評論列表。
comments 表還和 users 表之間有一對多關係。通過這個關係可以獲取用戶發表的所有評論,還能間接知道用戶發表了多少篇評論
管理評論,我們要在導航條中添加一個連結,具有權限的用戶才能看到。
14 RIA (API, REST)資源就是一切
資源是REST架構方式的核心概念。在REST架構中, 資源是程序中你要著重關注的事物。例如,在博客程序中,用戶、博客文章和評論都是資源。
創建新的Shell命令 deploy:
Flask自帶的開發Web伺服器表現很差,因為它不是為生產環境設計的伺服器。有兩個可以在生產環境中使用、性能良好且支持Flask程序的伺服器,分別是 Gunicorn 和 uWSGI。
manage:app參數冒號左邊的部分表示定義程序的包或者模塊,冒號右邊的部分表示包中程序實例的名字。注意,Gunicor默認使用埠8000,而Flask默認使用5000。
添加ProxyFix等WSGI中間件的方法是包裝WSGI程序。收到請求時,中間件有機會審查環境,在處理請求之前做些修改。不僅Heroku需要使用ProxyFix中間件,任何使用反向代理的部署環境都需要。
架設伺服器
在能夠託管程序之前,伺服器必須完成多項管理任務。
安裝資料庫伺服器,例如MySQL或Postgres。也可使用SQLite資料庫,但由於其自身 •的種種限制,不建議用於生產伺服器。
安裝郵件傳輸代理(Mail Transport Agent,MTA),例如Sendmail,用於向用戶發送郵件。
安裝適用於生產環境的Web伺服器,例如Gunicorn或uWSGI。
為了啟用安全HTTP,購買、安裝並配置SSL證書。
(可選,但強烈推薦)安裝前端反向代理伺服器,例如nginx或Apache。反向代理伺服器能直接服務於靜態文件,而把其他請求轉發給程序使用的Web伺服器。Web伺服器
監聽localhost中的一個私有埠。
強化伺服器。這一過程包含多項任務,目標在於降低伺服器被攻擊的可能性,例如安裝防火牆以及刪除不用的軟體和服務等
其它:Flask-Cache插件為你提供一組裝飾器來實現多種方式的緩存
開發自定義視圖裝飾器來幫助我們組織自己的代碼
自定義的URL轉換器將會讓你很嗨地玩轉URL:https://spacewander.github.io/explore-flask-zh/6-advanced_patterns_for_views_and_routing.html
Write a Tumblelog Application with Flask and MongoEngine
這是MongoDB官方文檔中的一個教程,也是學習Flask開發的一個很好案例,尤其適合Flask+MongoDB開發的應用場景
The Hitchhiker’s Guide to Python!
這個資料雖然不直接與Flask有關,但對初學者,絕對有學習的價值
GitHub - humiaozuzu/awesome-flask: A curated list of awesome Flask resources and plugins
The Flask Mega-Tutorial, Part I: Hello, World! - miguelgrinberg.com
大多數內容是本書上寫的更加 advanced,但以下話題只在「Mega Tutorial」裡提到:
全文檢索:
國際化:I18n and L10n
Ajax
Debug:pdb
查找Flask擴展一些值得研究的包。
Flask-Babel(https://pythonhosted.org/Flask-Babel/):提供國際化和本地化支持。
FLask-RESTful(http://flask-restful.readthedocs.org/en/latest/):開發REST API的工具。
Celery(http://docs.celeryproject.org/en/latest/):處理後臺作業的任務隊列。 •
Frozen-Flask(https://pythonhosted.org/Frozen-Flask/):把Flask程序轉換成靜態網站。
Flask-DebugToolbar:在瀏覽器中使用的調試工具。
Flask-Assets(https://github.com/miracle2k/flask-assets):用於合併、壓縮、編譯CSS和JavaScript靜態資源文件。
Flask-OAuth(http://pythonhosted.org/Flask-OAuth/):使用OAuth服務進行認證。
Flask-OpenID(http://pythonhosted.org/Flask-OpenID/):使用OpenID服務進行認證。
Flask-WhooshAlchemy: 使 用Whoosh實現Flask-SQLAlchemy模型的全文搜索。
Flask-KVsession(http://flask-kvsession.readthedocs.org/en/latest/):使用伺服器端存儲實現的另一種用戶會話。
Flask官方擴展網站
PEP 8: 每級縮進使用4個空格。不要使用tab
PEP 257: docstrings""" docstrings """
用了相對形式的import 點標記法:第一個.來表示當前目錄,之後的每一個.表示下一個父目錄。
from .modelsimport User
https://spacewander.github.io/explore-flask-zh/7-blueprints.html
什麼是藍圖?
一個藍圖定義了可用於單個應用的視圖,模板,靜態文件等等的集合。舉個例子,想像一下我們有一個用於管理面板的藍圖。這個藍圖將定義像/admin/login和/admin/dashboard這樣的路由的視圖。它可能還包括所需的模板和靜態文件。你可以把這個藍圖當做你的應用的管理面板,管它是太空人的交友網站,還是火箭推銷員的CRM系統。
藍圖的殺手鐧是將你的應用組織成不同的組件。假如我們有一個微博客,我們可能需要有一個藍圖用於網站頁面,比如index.html和about.html。然後我們還需要一個用於在登錄面板中展示最新消息的藍圖,以及另外一個用於管理員面板的藍圖。站點中每一個獨立的區域也可以在代碼上隔絕開來。最終你將能夠把你的應用依據許多能完成單一任務的小應用組織起來。
你也可以給藍圖中的所有路由定義一個動態子域名。
僅需五步走,你可以用藍圖重構一個應用。
flask-bootstrap 前端插件pip install flask-sqlalchemy
pip install flask-login
...
from flask.ext.bootstrap import Bootstrap
bootstrap = Bootstrap(app)
然後 /template/目錄下創建自己的 html,{% extends "bootstrap/base.html" %}
模板:\Lib\site-packages\flask_bootstrap\templates\bootstrap\
Bootstrap 導航欄 自定義配色:
{% block styles %}
{{super()}}
<link rel=stylesheet type=text/css href="{{ url_for('static', filename='style.css') }}">
{% endblock %}
其中 style.css 可以包含自定義的 .navbar-kevin {。。。}
配色方案 http://work.smarchal.com/twbscolor/
修改文件:/app/__init__.py
[python] view plain copy
from flask_debugtoolbar import DebugToolbarExtension
toolbar = DebugToolbarExtension()
toolbar.init_app(app)