今天我們會實作 config.py
和 requirements.txt
,並稍微介紹一下這些套件大概要幹嘛。我們把兩個分開來慢慢看。
在開始之前,我們要先講一下環境變數。很多資訊我們不會直接放在 config.py
裡面,而是要從環境變數去抓,這樣我們才不會把敏感的資訊 (如密碼、token 等等) 直接暴露在原始碼中。設置環境變數因作業系統而異,可以參考 Windows cmd、Windows PowerShell、linux,在此處不贅述。而我們要在 python 中抓到環境變數則需要使用 os.getenv
這個函式,在後面的範例我們會直接 from os import getenv
,所以全部都會使用 getenv
來呈現,有關其用法可以參考 Python | os.getenv() method。
直接來看看我們有那些需要的套件,我們會一項一項分開介紹。
Flask
Flask-SQLAlchemy
Flask-WTF
Flask-Login
Flask-Migrate
Flask-Mail
Flask-Markdown
Flask-Pagedown
第一個是 flask,這就不必再提了。
Flask-SQLAlchemy
是一個資料庫的 ORM 框架,它的好處是不用擔心 SQL injection,然後也會比較好寫,因為就是用 python 的風格在寫的。此外,他支援多種資料庫,所以我們只需要寫一次程式碼就可以給多種資料庫使用。但相對的,因為要透過一層 python 的轉換,效能上必定有差。request.form
,但我們沒有深入去了解,就是因為我們使用了 Flask-WTF
。他可以用物件的方式來建立我們需要的表單,而且也支援很多驗證,像是正規表示式 (regex)、長度、是否為空等等,如果需要用 reCAPTCHA 也可以用他。此外,他也可以防止 CSRF (利用 csrf_token),是一個安全而且方便的表單工具。Flask-Login
套件如其名,就是負責處理使用者驗證的套件,後面會非常大幅的提到他。Flask-Migrate
是用來管理資料庫版本的,當我們資料庫的結構有變動的時候,就可以讓他幫忙自動更新,減少人為的錯誤。他需要配合 Flask-SQLAlchemy
。Flask-Mail
一樣套件如其名,就是用來處理寄信的套件,在下面的設定檔會有更多細節。這個套件我覺得可選,不加入並不會導致整個 application 無法運作,我把他加入專案的原因只是想展現一次這個套件的功能。Flask-Markdown
會幫我們加入一個 markdown 的 jinja filter,這樣就可以直接在前端弄出 markdown。Flask-Pagedown
是一個可以讓我們邊編輯邊看 markdown 輸出的套件,這個之後會跟 Flask-WTF 一起玩。再來要看的是 config.py
,他會放在 app/
裡面,而我們也有一些是三個共通的設定,所以他們三個都會繼承自 Config
這個物件,現在就來看看它長甚麼樣子。請注意前面需要 from os import getenv
。
class Config:
# Flask-Mail
MAIL_SERVER = "smtp.gmail.com"
MAIL_PORT = 465
MAIL_USE_SSL = True
MAIL_USERNAME = getenv("MAIL_USERNAME")
MAIL_PASSWORD = getenv("MAIL_PASSWORD")
# Flask-SQLAlchemy
SQLALCHEMY_TRACK_MODIFICATIONS = False
它分成 Flask-Mail
和 Flask-SQLAlchemy
兩個部分,我們兩個分開講。
Flask-Mail
是用來處理寄信的套件,所以他當然需要知道怎麼寄信。我們會使用 gmail 來寄信,所以 MAIL_USERNAME
、MAIL_PASSWORD
都是 gmail 的資訊。這樣填完之後基本上來無法寄信,需要到 Google Account Security 裡面把「低安全性應用程式存取權」打開 (可參考 Send Gmail with Python),這樣才能夠寄信。如果不想用 gmail 也可以用其他郵件伺服器,但就需要去翻一下他的文件了。Flask-SQLAlchemy
的設定,非常簡單只有短短一行。這個設定其實無傷大雅,他是一個讓你可以開關事件系統 (event system) 的設定,基本上來說我們不需要他,但如果不關掉的話,他會在每次打開的時候送你警告,非常煩而且礙眼,所以關掉。此外,如果開著的話他會占用額外的記憶體資源,為了減少主機負擔,就把他關了吧。接下來我們來看看三個環境分別的設定。在這裡我們需要用到 os.urandom
這個函式來生成之前提過的 SECRET_KEY
,同樣地,我們使用 from os import urandom
來引入。
class Testing(Config):
# Flask
ENV = "TESTING"
TESTING = True
SECRET_KEY = "cyn54g544mxng"
SERVER_NAME = "localhost"
# Flask-SQLAlchemy
SQLALCHEMY_DATABASE_URI = "sqlite:///:memory:"
# Flask-WTF
WTF_CSRF_ENABLED = False
class Development(Config):
# Flask
ENV = "DEVELOPMENT"
DEBUG = True
SECRET_KEY = "cyn54g544mxng"
# Flask-SQLAlchemy
SQLALCHEMY_DATABASE_URI = "sqlite:///data.db"
class Production(Config):
# Flask
ENV = "PRODUCTION"
SECRET_KEY = urandom(32)
# Flask-SQLAlchemy
SQLALCHEMY_DATABASE_URI = getenv("DATABASE_URI")
我們以一個一個設定的名稱來解釋,前面一樣有註解來表示他是關於甚麼套件的設定。
ENV
就是代表環境 (environment)。有些其他的環境變數也可能會被影響,像是馬上要講到的 DEBUG
。DEBUG
是指示 flask 要不要開起 debug 工具用的,這些工具之前有提過了。如果 ENV=development
,那 DEBUG
會自動設為 True
。SECRET_KEY
之前也提過,是用來處理一些登入、session 相關的事情用的。理論上我們會用 urandom
來生成 (如 production),但因為他如果改變的話會讓舊的 session 都失效,開發的時候要一直重新登入很煩,所以就讓它靜態。TESTING
會讓 flask 開啟一些測試用的功能,而且有些套件的功能可能會改變,像是 Flask-Mail
在 TESTING=True
的時候就不會真的寄信出去,而是會打在 console。SERVER_NAME
在這裡是專門給測試用的,因為他跟平常使用的時候會有不一樣的狀態,所以會導致 url_for
無法使用。SQLALCHEMY_DATABASE_URI
是用來指定資料庫位置的設定,像我們開發的時候用 sqlite,就把檔案放在同一個目錄;測試的時候資料量不大,就把它放在記憶體就好;真正上線的時候就看環境要給甚麼,如果換一個資料庫的話就會把前面的 sqlite:
改掉。要特別注意一下他有三個 /
,而放在記憶體的話前後有冒號。flask-wtf
可以處理 CSRF 的攻擊,他用的就是 csrf_token,而 WTF_CSRF_ENABLED
就是用來設定要不要打開 csrf_token 的設定。因為在測試的時候,我們不會像正常在使用一樣實際按下送出,而是直接傳資料到後端,所以就會被 csrf_token 擋下來。因為如此,我們在測試的時候會把它關掉,讓測試順利一點,反正測試不會攻擊我們。app
的時候才有對照表,我們把環境的名稱對到他們分別要用到的設定檔,這個東西會在明天使用到。configs = {
"development": Development,
"testing": Testing,
"production": Production,
}
ORM 介紹及 ORM 優點、缺點
What are database migrations?
Flask-SQLAlchemy Configuration
Flask-Mail-example
Flask實作_ext_09_Flask-Mail_郵件發送
How do I know if I can disable SQLALCHEMY_TRACK_MODIFICATIONS?
SQLALCHEMY: how to use @events.listens_for in flask_sqlalchemy-SQLalchemy
Configuring Flask-Mail
Flask Builtin Configuration Values
Things You Should Know About Flask SERVER_NAME
請問以下這個是放在那裏?
"
configs = {
"development": Development,
"testing": Testing,
"production": Production,
}
"
放在 config.py
的最後面就可以了
還有day 9目錄結構圖沒有了,可以補回嗎? 多謝