iT邦幫忙

第 12 屆 iThome 鐵人賽

DAY 2
0

django-environ 是用來管理設定的套件,等等,一般設定不就是寫在 django settings 裡面嗎?為什麼還需要套件來管理呢?你想想,把設定寫死在程式裡,第一個未來要更動裡面的設定值時,得再改動程式,再者,考慮一下要佈署到不同的環境時,要怎麼更動設定呢?

django-environs 就是為此而生,透過這個套件,你可以把設定抽離到程式之外,程式裡只要聲明你要讀取哪些設定即可,而設定則可以是環境變數、設定檔等等的,這樣未來設定更動的時候,只要調整外部的這些設定來源就行,不會再次改動到程式,這樣就方便很多啦。

在我們設計一個服務時,通常會考慮到所謂的 12-factor,什麼是 12-factor 呢?12-factor 是講以下的這十二件事情:

  1. Codebase 程式碼:一套程式碼對應到一個應用程式,如果有多個應用程式共用一部分程式碼,那麼就該把這部份包裝函式庫。
  2. Dependencies 相依性:要把相依性納入管理,利用 pyproject.toml (poetry), requirements.txt (pip) 等來做記錄,未來要接手的工程師就可以透過這些檔案來還原當時的開發/生產環境。
  3. Config 配置變數:不要把設定直接放在程式碼裡面,而是把設定放在環境變數、設定檔或是 k8s/docker 的 secrets 裡,同時設定也要納入管理,並加上版本號碼。
  4. Backing Services 後端支援服務:所有的後端支援服務都應該視為外接的資源,舉凡資料庫、快取、SMTP 等等都是。這樣未來在切換服務時,只需要改動設定就可以切換服務來源。未來在擴充上,也能得到很大的助益。
  5. Build, Release and Run 建構、發行與執行:確實分離建構與執行的階段。
    1. 構建是指把程式碼進行編譯後打包的過程,有些程式語言沒有編譯,那麼這個過程就會是單純的打包。佈署,則是將建置後的產出物跟設定相結合,放到指定的環境去。執行就是依照指定的版本,在環境中啟動、執行。
    2. 需要有版本號,方便追蹤,有問題時,可以快速回復到指定版本。
    3. 盡量把這過程自動化,避免人為過失。
  6. Process 處理程序:要以「無狀態」的方式來執行,也就是說,應用程式本身不保存長期狀態或儲存資料,而是把長期狀態或資料儲存到外部服務去,這樣的好處是,應用程式不需要考慮資料遺失的問題,在應用程式意外關閉時,可以隨時重新啟動。
  7. Port binding 連接埠:透過連接埠對外提供服務,避免對外暴露過多的介面。
  8. Concurrency 同步執行:借助外部的程序管理系統來管理程序,也因為程序的特性,可以容易水平擴展。
  9. Disposability 快速啟用與終止:因為是程序,可以快速的啟用跟終止,啟動時間也可以達到最少。另外也要優雅的處理 SIGTERM ,讓程序可以不用等待過多當前的處理才退出。
  10. 環境的一致性:保持開發環境與生產環境的一致,除錯會比較容易。另外也要盡可能的頻繁佈署,讓所有人都對這個過程與環境有一定的認識。
  11. Logs 記錄:程序統一使用 stdout 輸出 log,那 log 可以再透過其他的服務,例如 flutentd / telegraf 等來輸出或擷取。
  12. Admin processes:要跟正常程序使用同樣的環境,也需要遵守上述的準則。

簡單的說,這套件主要是遵循 12 factor app 的 Config 配置變數 準則。

安裝

poetry add django-environ

使用

settings.py 裡,加入

import environ

ROOT_DIR = (
    environ.Path(__file__) - 1
)
env = environ.Env()

# 依據環境變數來決定是否讀取 .env
READ_DOT_ENV_FILE = env.bool("DJANGO_READ_DOT_ENV_FILE", default=False)
if READ_DOT_ENV_FILE:
    # OS environment variables take precedence over variables from .env
    env.read_env(str(ROOT_DIR.path(".env")))

DEBUG = env.bool("DJANGO_DEBUG", False)
SECRET_KEY = env('DJANGO_SECRET_KEY')
DATABASES = {"default": env.db("DATABASE_URL")}
EMAIL_CONFIG = env.email_url(
    'EMAIL_URL', default='smtp://user:password@localhost:25')
ALLOWED_HOSTS = env.list("DJANGO_ALLOWED_HOSTS", default=["localhost","example.com"])

解釋一下上面的程式

  1. environ.Path(file) 是取得 settings 檔案的目錄,environ 覆寫了 Path 的 - 運算,可以便捷的取得 settings 檔案的相對目錄,-1 表示上一層,-2表示上兩層,以此類推。
  2. env.read_env() 是讀取指定的檔案,這邊是表示讀取 .env 這個檔案。那這邊預先判斷有沒有 DJANGO_READ_DOT_ENV_FILE 這個環境變數,有的話而且內容為 True 才去讀取。
  3. env.bool() 表示讀取 DJANGO_DEBUG ,而 DJANGO_DEBUG 是一個布林型態的設定,如果沒有設定,預設值為 False。
  4. env() 表示讀取 DJANGO_SECRET_KEY 這個字串型態的設定,這邊沒有填預設值,所以在沒有設定的時候,就會丟出 ImproperlyConfigured 這個例外,這樣就能很明確的讓人知道必須要填這個設定。
  5. env.db() 是表示讀取資料庫連接字串設定,這邊這個 db 主要是幫你多做解析資料庫連接字串,並轉換為 Django 可以接受的 dict 型態。
  6. env.email_url() 也跟 db 一樣的道理。
  7. env.list() 是表示設定內容是一個 List 型態的設定,實際上是以 "," 分隔的字串,env.list() 會幫你做 split(",") ,轉換為 List 型態。

好啦,現在我們可以執行看看 runserver

poetry run python manage.py runserver

這時就會看到像這樣的錯誤訊息,提醒你要加這個設定啦

django.core.exceptions.ImproperlyConfigured: Set the DATABASE_URL environment variable

現在我們補上 DJANGO_SECRET_KEY 以及 DATABASE_URL 的設定:

export DJANGO_SECRET_KEY=this_is_a_test_secret_key
export DATABASE_URL=sqlite://$(pwd)/db.sqlite3

然後再執行,就可以看到順利啟動啦。

django-environ 更多的用法可以參考 django-environ 網站上的說明文件:https://django-environ.readthedocs.io/en/latest/

參考資料


上一篇
01. Django
下一篇
03. djangorestframework (1)
系列文
加速你的 Django 網站開發 - Django 的好用套件30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言