タイプヒントを使用してDjango設定を合理化する:Pydanticチュートリアル
公開: 2022-07-22新しい環境を追加するための堅牢でスケーラブルな方法がなかったため、Djangoプロジェクトは私を苛立たせていました。 pydanticとPythonタイプのヒントを組み合わせることで、必要な強力な基盤を構築しました。
PEP 484で説明されているように、タイプヒントは静的分析をサポートしますが、これらの同じ注釈は実行時にも使用できます。 pydanticのようなサードパーティパッケージは、この追加のメタデータを使用するランタイムタイプチェックを提供します。 Pydanticは、Pythonタイプのヒントを使用して、設定メタデータの管理とランタイムデータ検証の実行を支援します。
このpydanticチュートリアルでは、Djangoでpydantic設定管理を使用することの広範囲にわたるプラスの効果を示します。
私たちの構成は、Twelve-FactorAppWebサイトに記載されているベストプラクティスに準拠しています。
- 非定数および秘密の構成を環境変数として定義します。
- 開発環境では、
.env
ファイルで環境変数を定義し、.env
を.gitignore
に追加します。 - クラウドプロバイダーのメカニズムを使用して、QA、ステージング、および本番環境の(秘密の)環境変数を定義します。
- 環境変数から自分自身を構成する単一の
settings.py
ファイルを使用します。 - pydanticを使用して、環境変数を読み取り、チェックし、検証し、Django構成を定義するPython変数に型キャストします。
または、一部の開発者は、 settings_dev.py
やsettings_prod.py
などの複数の設定ファイルを作成します。 残念ながら、このアプローチはうまく拡張できません。 これは、コードの重複、混乱、見つけにくいバグ、およびより高いメンテナンス作業につながります。
前述のベストプラクティスを使用すると、環境をいくつでも追加するのは簡単で、明確に定義されており、エラーが発生しません。 より複雑な環境構成を検討することもできますが、明確にするために、開発と本番の2つに焦点を当てます。
これは実際にはどのように見えますか?
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インスタンスを使用できますが、本番環境では、プロビジョニングされたデータベースサービスを指します。
定義したいもう1つの変数は、ブール値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 Varと呼ばれ、Herokuダッシュボードを介して構成されます。 これらは、デプロイされたアプリケーションで環境変数として使用できるようになります。
その後、アプリケーションの構成を調整して、いずれかの環境からこれらの値を自動的に読み取る必要があります。
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
このコードは次のことを行います。
- pydanticの
BaseSettings
クラスから継承するクラスSettingsFromEnvironment
を定義します。 -
DATABASE_URL
とDEBUG
を定義し、Pythonタイプヒントを使用してそれらのタイプとオプションのデフォルトを設定します。 - システムの環境変数に存在しない場合、
.env
ファイル内の変数を検索するようにpydanticに指示するクラスConfig
を定義します。 -
Config
クラスをオブジェクトconfig
にインスタンス化します。 必要な変数は、config.DATABASE_URL
およびconfig.DEBUG
として使用できるようになります。 - これらの
config
メンバーから通常のDjango変数DATABASES
およびDEBUG
を定義します。
同じコードがすべての環境で実行され、pydanticが次の処理を行います。
- 環境変数
DATABASE_URL
とDEBUG
を探します。- 本番環境のように環境変数として定義されている場合は、それらを使用します。
- それ以外の場合は、
.env
ファイルからそれらの値を取得します。 - 値が見つからない場合は、次のようになります。
-
DATABASE_URL
の場合、エラーをスローします。 -
DEBUG
の場合、デフォルト値のFalse
が割り当てられます。
-
- 環境変数が見つかると、フィールドタイプがチェックされ、どちらかが間違っているとエラーが発生します。
-
DATABASE_URL
の場合、フィールドタイプがPostgresDsn
スタイルのURLであることを確認します。 -
DEBUG
の場合、フィールドタイプが有効で非厳密なpydanticブール値であることを確認します。
-
DATABASE_URL
の構成値からのオペレーティングシステムの環境変数の明示的な設定に注意してください。 DATABASE_URL
はすでに外部環境変数として定義されているため、 os.environ["DATABASE_URL"] = config.DATABASE_URL
を設定するのは冗長に思えるかもしれません。 ただし、これにより、pydanticはこの変数を解析、チェック、および検証できます。 環境変数DATABASE_URL
が欠落しているか、正しくフォーマットされていない場合、pydanticは明確なエラーメッセージを表示します。 これらのエラーチェックは、アプリケーションが開発環境から後続の環境に移行するときに非常に役立ちます。
変数が定義されていない場合は、デフォルトが割り当てられるか、エラーによって変数の定義を求められます。 生成されたプロンプトには、目的の変数タイプの詳細も示されます。 これらのチェックの副次的な利点は、新しいチームメンバーとDevOpsエンジニアが、定義する必要のある変数をより簡単に発見できることです。 これにより、すべての変数を定義せずにアプリケーションを実行した場合に発生する、見つけにくい問題を回避できます。
それでおしまい。 これで、アプリケーションには、settings.pyの単一バージョンを使用した保守可能な設定管理の実装がありsettings.py
。 このアプローチの利点は、 .env
ファイルまたはホスティング環境で利用できるその他の必要な手段で正しい環境変数を指定できることです。
スケーラブルパス
私はすべてのDjangoプロジェクトでpydanticランタイムタイピングを使用してDjango設定管理を使用しています。 私はそれがより小さく、より保守しやすいコードベースにつながることを発見しました。 また、新しい環境を追加するための、適切に構造化された自己文書化されたスケーラブルなアプローチも提供します。
このシリーズの次の記事は、Djangoアプリケーションをゼロから構築し、pydantic設定管理を使用して、Herokuにデプロイするためのステップバイステップのチュートリアルです。
Toptal Engineering Blogは、この記事で紹介されているコードサンプルをレビューしてくれたStephenDavidsonに感謝の意を表します。
この記事の以前のバージョンでは、特定のPythonリリースが強調されていました。 Pythonは数年前からタイプヒントをサポートしてきたため、このバージョン参照を削除することで紹介テキストを一般化しました。