使用類型提示簡化 Django 設置:Pydantic 教程

已發表: 2022-07-22

Django 項目曾經讓我感到沮喪,因為我缺乏一種強大且可擴展的方式來添加新環境。 通過將 pydantic 和 Python 類型提示結合在一起,我建立了我需要的強大基礎。

如 PEP 484 中所述,類型提示支持靜態分析,但這些相同的註釋在運行時也可用。 像 pydantic 這樣的第三方包提供了使用這些額外元數據的運行時類型檢查。 Pydantic 使用 Python 類型提示來幫助管理設置元數據並執行運行時數據驗證。

本 pydantic 教程將展示在 Django 中使用 pydantic 設置管理的深遠而積極的影響。

我們的配置遵循 Twelve-Factor App 網站上描述的最佳實踐:

  1. 將非常量和秘密配置定義為環境變量。
  2. 在開發環境中,在.env文件中定義環境變量並將.env添加到.gitignore
  3. 使用雲提供商的機制為 QA、暫存和生產環境定義(秘密)環境變量。
  4. 使用從環境變量配置自身的單個settings.py文件。
  5. 使用 pydantic 讀取、檢查、驗證和類型轉換環境變量到定義 Django 配置的 Python 變量。

或者,一些開發人員創建多個設置文件,例如settings_dev.pysettings_prod.py 。 不幸的是,這種方法不能很好地擴展。 它會導致代碼重複、混亂、難以發現的錯誤和更高的維護工作量。

使用上述最佳實踐,添加任意數量的環境很容易、定義明確且防錯。 儘管我們可以探索更複雜的環境配置,但為了清楚起見,我們將重點關注兩個:開發和生產。

這在實踐中是什麼樣的?

Pydantic 設置管理和環境變量

我們現在關注開發和生產中的一個示例。 我們展示了每個環境如何以不同的方式配置其設置以及 pydantic 如何支持每個環境。

我們的示例應用程序需要 Django 支持的數據庫,因此我們需要存儲數據庫連接字符串。 我們使用 Python 包 dj-database-url 將數據庫連接配置信息移動到環境變量DATABASE_URL中。 請注意,此變量是str類型,格式如下:

 postgres://{user}:{password}@{hostname}:{port}/{database-name} mysql://{user}:{password}@{hostname}:{port}/{database-name} oracle://{user}:{password}@{hostname}:{port}/{database-name} sqlite:///PATH

在我們的開發環境中,我們可以使用一個包含 Docker 的 PostgreSQL 實例以方便使用,而在我們的生產環境中,我們將指向一個預置的數據庫服務。

我們要定義的另一個變量是布爾值DEBUG 。 決不能在生產部署中打開 Django 中的 DEBUG 標誌。 它旨在在開發過程中獲得額外的反饋。 例如,在調試模式下,Django 會在發生異常時顯示詳細的錯誤頁面。

開發和生產的不同價值可以定義如下:

變量的名稱發展生產
DATABASE_URL postgres://postgres:mypw@localhost:5432/mydb postgres://foo1:foo2@foo3:5432/foo4
DEBUG True False

我們使用 pydantic 設置管理模塊來根據環境管理這些不同的環境變量值集。

準備步驟

為了將其付諸實踐,我們開始通過創建包含以下內容的單個.env文件來配置我們的開發環境:

 DATABASE_URL=postgres://postgres:mypw@localhost:5432/mydb DEBUG=True

接下來,我們將.env文件添加到項目的.gitignore文件中。 .gitignore文件避免在源代碼管理中保存潛在的敏感信息。

雖然這種方法在我們的開發環境中運行良好,但我們的生產環境規範使用了不同的機制。 我們的最佳實踐規定生產環境變量使用環境機密。 例如,在 Heroku 上,這些秘密稱為 Config Vars,並通過 Heroku Dashboard 進行配置。 它們作為環境變量提供給已部署的應用程序:

Config Vars Web 界面的屏幕截圖。左側邊欄有一個描述:“配置變量改變了你的應用程序的行為方式。除了創建你自己的,一些附加組件帶有它們自己的。”主要部分有兩行,每行有兩個文本字段、一個鉛筆圖標和一個 X 圖標。文本字段與上表中的“變量名稱”和“生產”列具有相同的數據。右上角有一個按鈕,上面寫著“隱藏配置變量”。

之後,我們需要調整應用程序的配置以自動從任一環境讀取這些值。

配置 Django 的settings.py

讓我們從一個新的 Django 項目開始,為我們的示例提供基本結構。 我們使用以下終端命令搭建一個新的 Django 項目:

 $ django-admin startproject mysite

我們現在有了mysite項目的基本項目結構。 項目的文件結構如下:

 mysite/ manage.py mysite/ __init__.py settings.py urls.py asgi.py wsgi.py

settings.py文件包含允許我們管理應用程序配置的樣板代碼。 它有許多預定義的默認設置,我們必鬚根據相關環境進行調整。

要使用環境變量和 pydantic 管理這些應用程序設置,請將以下代碼添加到settings.py文件的頂部:

 import os from pathlib import Path from pydantic import ( BaseSettings, PostgresDsn, EmailStr, HttpUrl, ) import dj_database_url # Build paths inside the project like this: BASE_DIR / 'subdir'. BASE_DIR = Path(__file__).resolve().parent.parent class SettingsFromEnvironment(BaseSettings): """Defines environment variables with their types and optional defaults""" DATABASE_URL: PostgresDsn DEBUG: bool = False class Config: """Defines configuration for pydantic environment loading""" env_file = str(BASE_DIR / ".env") case_sensitive = True config = SettingsFromEnvironment() os.environ["DATABASE_URL"] = config.DATABASE_URL DATABASES = { "default": dj_database_url.config(conn_max_age=600, ssl_require=True) } DEBUG = config.DEBUG

此代碼執行以下操作:

  • 定義一個類SettingsFromEnvironment ,繼承自 pydantic 的BaseSettings類。
  • 定義DATABASE_URLDEBUG ,使用 Python 類型提示設置它們的類型和可選默認值。
  • 定義一個類Config告訴 pydantic 如果不存在於系統的環境變量中,則在.env文件中查找變量。
  • Config類實例化為對象config ; 所需的變量可作為config.DATABASE_URLconfig.DEBUG
  • 從這些config成員中定義常規的 Django 變量DATABASESDEBUG

相同的代碼在所有環境中運行,pydantic 負責以下工作:

  • 它查找環境變量DATABASE_URLDEBUG
    • 如果定義為環境變量,就像在生產中一樣,它將使用那些。
    • 否則,它會從.env文件中提取這些值。
    • 如果它沒有找到值,它將執行以下操作:
      • 對於DATABASE_URL ,它會引發錯誤。
      • 對於DEBUG ,它分配一個默認值False
  • 如果它找到一個環境變量,它將檢查字段類型並在其中任何一個錯誤時給出錯誤:
    • 對於DATABASE_URL ,它驗證其字段類型是PostgresDsn樣式的 URL。
    • 對於DEBUG ,它驗證其字段類型是有效的、非嚴格的 pydantic 布爾值。

請注意DATABASE_URL配置值中操作系統環境變量的顯式設置。 設置os.environ["DATABASE_URL"] = config.DATABASE_URL似乎是多餘的,因為DATABASE_URL已經定義為外部環境變量。 但是,這允許 pydantic 解析、檢查和驗證這個變量。 如果環境變量DATABASE_URL丟失或格式不正確,pydantic 將給出明確的錯誤消息。 隨著應用程序從開發轉移到後續環境,這些錯誤檢查非常寶貴。

如果未定義變量,則將分配默認值或將提示錯誤以提示對其進行定義。 任何生成的提示也會詳細說明所需的變量類型。 這些檢查的一個附帶好處是新團隊成員和 DevOps 工程師更容易發現需要定義哪些變量。 這避免了當應用程序在未定義所有變量的情況下運行時導致的難以發現的問題。

而已。 該應用程序現在使用單個版本的settings.py具有可維護的設置管理實現。 這種方法的美妙之處在於它允許我們在.env文件中指定正確的環境變量或通過我們的託管環境提供的任何其他所需方式。

可擴展的路徑

我一直在我的所有 Django 項目中使用帶有 pydantic 運行時類型的 Django 設置管理。 我發現它會導致更小、更可維護的代碼庫。 它還提供了一種結構良好、自記錄且可擴展的方法來添加新環境。

本系列的下一篇文章是關於從頭開始構建 Django 應用程序的分步教程,使用 pydantic 設置管理並將其部署到 Heroku。


Toptal 工程博客感謝 Stephen Davidson 審閱本文中提供的代碼示例。

本文的早期版本強調了特定的 Python 版本。 由於 Python 多年來一直支持類型提示,因此我們通過刪除此版本參考來概括介紹性文本。