iT邦幫忙

2021 iThome 鐵人賽

DAY 10
0
Modern Web

Flask系列 第 10

Day 10 實作基本檔案

前言

今天我們會實作 config.pyrequirements.txt,並稍微介紹一下這些套件大概要幹嘛。我們把兩個分開來慢慢看。

在開始之前,我們要先講一下環境變數。很多資訊我們不會直接放在 config.py 裡面,而是要從環境變數去抓,這樣我們才不會把敏感的資訊 (如密碼、token 等等) 直接暴露在原始碼中。設置環境變數因作業系統而異,可以參考 Windows cmdWindows PowerShelllinux,在此處不贅述。而我們要在 python 中抓到環境變數則需要使用 os.getenv 這個函式,在後面的範例我們會直接 from os import getenv,所以全部都會使用 getenv 來呈現,有關其用法可以參考 Python | os.getenv() method

requirements.txt

直接來看看我們有那些需要的套件,我們會一項一項分開介紹。


Flask
Flask-SQLAlchemy
Flask-WTF
Flask-Login
Flask-Migrate
Flask-Mail
Flask-Markdown
Flask-Pagedown

第一個是 flask,這就不必再提了。

  • Flask-SQLAlchemy 是一個資料庫的 ORM 框架,它的好處是不用擔心 SQL injection,然後也會比較好寫,因為就是用 python 的風格在寫的。此外,他支援多種資料庫,所以我們只需要寫一次程式碼就可以給多種資料庫使用。但相對的,因為要透過一層 python 的轉換,效能上必定有差。
  • 之前在說 flask 的 request 的時候有提到一個東西叫做 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

再來要看的是 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-MailFlask-SQLAlchemy 兩個部分,我們兩個分開講。

  • 如剛剛所說,Flask-Mail 是用來處理寄信的套件,所以他當然需要知道怎麼寄信。我們會使用 gmail 來寄信,所以 MAIL_USERNAMEMAIL_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-MailTESTING=True 的時候就不會真的寄信出去,而是會打在 console。
  • SERVER_NAME 在這裡是專門給測試用的,因為他跟平常使用的時候會有不一樣的狀態,所以會導致 url_for 無法使用。
  • SQLALCHEMY_DATABASE_URI 是用來指定資料庫位置的設定,像我們開發的時候用 sqlite,就把檔案放在同一個目錄;測試的時候資料量不大,就把它放在記憶體就好;真正上線的時候就看環境要給甚麼,如果換一個資料庫的話就會把前面的 sqlite: 改掉。要特別注意一下他有三個 /,而放在記憶體的話前後有冒號。
  • 前面有提到 flask-wtf 可以處理 CSRF 的攻擊,他用的就是 csrf_token,而 WTF_CSRF_ENABLED 就是用來設定要不要打開 csrf_token 的設定。因為在測試的時候,我們不會像正常在使用一樣實際按下送出,而是直接傳資料到後端,所以就會被 csrf_token 擋下來。因為如此,我們在測試的時候會把它關掉,讓測試順利一點,反正測試不會攻擊我們。
  • 最後我們還需要加入一個小小的 dict,這樣到時候製造 app 的時候才有對照表,我們把環境的名稱對到他們分別要用到的設定檔,這個東西會在明天使用到。
configs = {
    "development": Development,
    "testing": Testing,
    "production": Production,
}

References

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


上一篇
Day 9 專案目錄結構
下一篇
Day 11 實作 create_app
系列文
Flask30

尚未有邦友留言

立即登入留言