設定と環境変数¶
多くの場合、アプリケーションは秘密鍵、データベース認証情報、メールサービスの認証情報など、外部設定または構成を必要とする場合があります。
これらの設定のほとんどは、データベースURLなど、可変(変更可能)です。そして、多くのものは秘密など、機密性の高い可能性があります。
このため、アプリケーションによって読み取られる環境変数にそれらを提供することが一般的です。
ヒント
環境変数を理解するには、環境変数を参照してください。
型と検証¶
これらの環境変数は、Pythonの外部であり、他のプログラムやシステム全体(Linux、Windows、macOSなどの異なるオペレーティングシステムも含む)と互換性を持たなければならないため、テキスト文字列しか処理できません。
つまり、環境変数からPythonで読み取られる値は`str`になり、異なる型への変換や検証はコード内で行う必要があります。
Pydantic `Settings`¶
幸いなことに、PydanticはPydantic:設定管理で、環境変数から取得されるこれらの設定を処理するための優れたユーティリティを提供します。
`pydantic-settings`のインストール¶
まず、仮想環境を作成し、それをアクティブにしてから、`pydantic-settings`パッケージをインストールします。
$ pip install pydantic-settings
---> 100%
また、`all`エクストラをインストールするときに含まれます。
$ pip install "fastapi[all]"
---> 100%
情報
Pydantic v1では、メインパッケージに含まれていました。現在は、この独立したパッケージとして配布されるため、その機能が必要ない場合はインストールするかどうかの選択ができます。
`Settings`オブジェクトの作成¶
Pydanticから`BaseSettings`をインポートし、Pydanticモデルと非常によく似たサブクラスを作成します。
Pydanticモデルと同様に、型アノテーション、そして場合によってはデフォルト値を持つクラス属性を宣言します。
Pydanticモデルで使用しているのと同じ検証機能やツール(さまざまなデータ型や`Field()`を使った追加の検証など)をすべて使用できます。
from fastapi import FastAPI
from pydantic_settings import BaseSettings
class Settings(BaseSettings):
app_name: str = "Awesome API"
admin_email: str
items_per_user: int = 50
settings = Settings()
app = FastAPI()
@app.get("/info")
async def info():
return {
"app_name": settings.app_name,
"admin_email": settings.admin_email,
"items_per_user": settings.items_per_user,
}
情報
Pydantic v1では、`pydantic_settings`ではなく`pydantic`から`BaseSettings`を直接インポートします。
from fastapi import FastAPI
from pydantic import BaseSettings
class Settings(BaseSettings):
app_name: str = "Awesome API"
admin_email: str
items_per_user: int = 50
settings = Settings()
app = FastAPI()
@app.get("/info")
async def info():
return {
"app_name": settings.app_name,
"admin_email": settings.admin_email,
"items_per_user": settings.items_per_user,
}
ヒント
コピーして貼り付けるための簡単なものが必要な場合は、この例を使用しないで、以下の最後の例を使用してください。
その後、その`Settings`クラスのインスタンス(この場合は`settings`オブジェクト)を作成すると、Pydanticは大文字と小文字を区別しない方法で環境変数を読み取ります。そのため、大文字の変数`APP_NAME`は属性`app_name`に対して読み取られます。
次に、データを変換して検証します。そのため、その`settings`オブジェクトを使用すると、宣言した型のデータが得られます(例:`items_per_user`は`int`になります)。
`settings`の使用¶
その後、アプリケーションで新しいsettings
オブジェクトを使用できます。
from fastapi import FastAPI
from pydantic_settings import BaseSettings
class Settings(BaseSettings):
app_name: str = "Awesome API"
admin_email: str
items_per_user: int = 50
settings = Settings()
app = FastAPI()
@app.get("/info")
async def info():
return {
"app_name": settings.app_name,
"admin_email": settings.admin_email,
"items_per_user": settings.items_per_user,
}
サーバーを実行します¶
次に、環境変数として設定を渡してサーバーを実行します。たとえば、ADMIN_EMAIL
とAPP_NAME
を設定できます。
$ ADMIN_EMAIL="deadpool@example.com" APP_NAME="ChimichangApp" fastapi run main.py
<span style="color: green;">INFO</span>: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)
ヒント
単一のコマンドで複数の環境変数を設定するには、それらをスペースで区切り、すべてコマンドの前に配置します。
すると、admin_email
設定は"deadpool@example.com"
に設定されます。
app_name
は"ChimichangApp"
になります。
items_per_user
はデフォルト値の50
のままです。
別のモジュールでの設定¶
より大きなアプリケーション - 複数のファイルで見たように、これらの設定を別のモジュールファイルに配置できます。
たとえば、次のようなconfig.py
ファイルを持つことができます。
from pydantic_settings import BaseSettings
class Settings(BaseSettings):
app_name: str = "Awesome API"
admin_email: str
items_per_user: int = 50
settings = Settings()
そして、それをmain.py
ファイルで使用します。
from fastapi import FastAPI
from .config import settings
app = FastAPI()
@app.get("/info")
async def info():
return {
"app_name": settings.app_name,
"admin_email": settings.admin_email,
"items_per_user": settings.items_per_user,
}
ヒント
より大きなアプリケーション - 複数のファイルで見たように、__init__.py
ファイルも必要です。
依存関係での設定¶
場合によっては、どこでも使用されるsettings
というグローバルオブジェクトを持つ代わりに、依存関係から設定を提供することが有用な場合があります。
これは、独自の カスタム設定で依存関係を簡単にオーバーライドできるため、テスト中に特に役立ちます。
設定ファイル¶
前の例から、config.py
ファイルは次のようになります。
from pydantic_settings import BaseSettings
class Settings(BaseSettings):
app_name: str = "Awesome API"
admin_email: str
items_per_user: int = 50
デフォルトインスタンスsettings = Settings()
は作成されません。
メインアプリケーションファイル¶
新しいconfig.Settings()
を返す依存関係を作成します。
from functools import lru_cache
from typing import Annotated
from fastapi import Depends, FastAPI
from .config import Settings
app = FastAPI()
@lru_cache
def get_settings():
return Settings()
@app.get("/info")
async def info(settings: Annotated[Settings, Depends(get_settings)]):
return {
"app_name": settings.app_name,
"admin_email": settings.admin_email,
"items_per_user": settings.items_per_user,
}
from functools import lru_cache
from fastapi import Depends, FastAPI
from typing_extensions import Annotated
from .config import Settings
app = FastAPI()
@lru_cache
def get_settings():
return Settings()
@app.get("/info")
async def info(settings: Annotated[Settings, Depends(get_settings)]):
return {
"app_name": settings.app_name,
"admin_email": settings.admin_email,
"items_per_user": settings.items_per_user,
}
ヒント
可能であれば、Annotated
バージョンを使用することをお勧めします。
from functools import lru_cache
from fastapi import Depends, FastAPI
from .config import Settings
app = FastAPI()
@lru_cache
def get_settings():
return Settings()
@app.get("/info")
async def info(settings: Settings = Depends(get_settings)):
return {
"app_name": settings.app_name,
"admin_email": settings.admin_email,
"items_per_user": settings.items_per_user,
}
ヒント
@lru_cache
については、後で説明します。
現時点では、get_settings()
は通常の関数であると仮定できます。
そして、パスオペレーション関数から依存関係としてそれを要求し、必要な場所でどこでも使用できます。
from functools import lru_cache
from typing import Annotated
from fastapi import Depends, FastAPI
from .config import Settings
app = FastAPI()
@lru_cache
def get_settings():
return Settings()
@app.get("/info")
async def info(settings: Annotated[Settings, Depends(get_settings)]):
return {
"app_name": settings.app_name,
"admin_email": settings.admin_email,
"items_per_user": settings.items_per_user,
}
from functools import lru_cache
from fastapi import Depends, FastAPI
from typing_extensions import Annotated
from .config import Settings
app = FastAPI()
@lru_cache
def get_settings():
return Settings()
@app.get("/info")
async def info(settings: Annotated[Settings, Depends(get_settings)]):
return {
"app_name": settings.app_name,
"admin_email": settings.admin_email,
"items_per_user": settings.items_per_user,
}
ヒント
可能であれば、Annotated
バージョンを使用することをお勧めします。
from functools import lru_cache
from fastapi import Depends, FastAPI
from .config import Settings
app = FastAPI()
@lru_cache
def get_settings():
return Settings()
@app.get("/info")
async def info(settings: Settings = Depends(get_settings)):
return {
"app_name": settings.app_name,
"admin_email": settings.admin_email,
"items_per_user": settings.items_per_user,
}
設定とテスト¶
get_settings
の依存関係オーバーライドを作成することで、テスト中に異なる設定オブジェクトを簡単に提供できます。
from fastapi.testclient import TestClient
from .config import Settings
from .main import app, get_settings
client = TestClient(app)
def get_settings_override():
return Settings(admin_email="testing_admin@example.com")
app.dependency_overrides[get_settings] = get_settings_override
def test_app():
response = client.get("/info")
data = response.json()
assert data == {
"app_name": "Awesome API",
"admin_email": "testing_admin@example.com",
"items_per_user": 50,
}
依存関係オーバーライドでは、新しいSettings
オブジェクトを作成する際にadmin_email
に新しい値を設定し、その新しいオブジェクトを返します。
そして、それが使用されていることをテストできます。
.env
ファイルの読み取り¶
多くの設定があり、さまざまな環境で頻繁に変更される可能性がある場合、それらをファイルに配置し、環境変数のようにそこから読み取る方が便利な場合があります。
この方法は十分に一般的であるため、名前が付けられています。これらの環境変数は通常.env
ファイルに配置され、ファイルは「dotenv」と呼ばれます。
ヒント
ドット(.
)で始まるファイルは、LinuxやmacOSなどのUnix系システムでは隠しファイルです。
しかし、dotenvファイルは必ずしもその正確なファイル名である必要はありません。
Pydanticは、外部ライブラリを使用してこれらのタイプのファイルからの読み取りをサポートしています。詳しくはPydantic Settings: Dotenv (.env) supportをご覧ください。
ヒント
これを実行するには、pip install python-dotenv
が必要です。
.env
ファイル¶
次のような.env
ファイルを持つことができます。
ADMIN_EMAIL="deadpool@example.com"
APP_NAME="ChimichangApp"
.env
からの設定の読み取り¶
そして、config.py
を次のように更新します。
from pydantic_settings import BaseSettings, SettingsConfigDict
class Settings(BaseSettings):
app_name: str = "Awesome API"
admin_email: str
items_per_user: int = 50
model_config = SettingsConfigDict(env_file=".env")
ヒント
model_config
属性は、Pydanticの設定にのみ使用されます。詳しくはPydantic: Concepts: Configurationをご覧ください。
from pydantic import BaseSettings
class Settings(BaseSettings):
app_name: str = "Awesome API"
admin_email: str
items_per_user: int = 50
class Config:
env_file = ".env"
ヒント
Config
クラスは、Pydanticの設定にのみ使用されます。詳しくはPydantic Model Configをご覧ください。
情報
Pydanticバージョン1では設定は内部クラスConfig
で行われ、Pydanticバージョン2では属性model_config
で行われます。この属性はdict
を受け取り、自動補完とインラインエラーを取得するには、SettingsConfigDict
をインポートして使用してそのdict
を定義できます。
ここでは、PydanticのSettings
クラス内に設定env_file
を定義し、値を使用するdotenvファイルのファイル名に設定します。
lru_cache
を使用してSettings
を一度だけ作成する¶
ディスクからのファイルの読み取りは通常コストのかかる(遅い)操作であるため、一度だけ実行して同じ設定オブジェクトを再利用し、各リクエストごとに読み取るのではなく、行うことをお勧めします。
しかし、毎回
Settings()
新しいSettings
オブジェクトが作成され、作成時に.env
ファイルが再度読み取られます。
依存関数のように
def get_settings():
return Settings()
各リクエストに対してそのオブジェクトを作成し、各リクエストに対して.env
ファイルを読み取ることになります。⚠️
しかし、上に@lru_cache
デコレータを使用しているため、Settings
オブジェクトは最初に呼び出されたときにのみ作成されます。✔️
from functools import lru_cache
from fastapi import Depends, FastAPI
from typing_extensions import Annotated
from . import config
app = FastAPI()
@lru_cache
def get_settings():
return config.Settings()
@app.get("/info")
async def info(settings: Annotated[config.Settings, Depends(get_settings)]):
return {
"app_name": settings.app_name,
"admin_email": settings.admin_email,
"items_per_user": settings.items_per_user,
}
from functools import lru_cache
from typing import Annotated
from fastapi import Depends, FastAPI
from . import config
app = FastAPI()
@lru_cache
def get_settings():
return config.Settings()
@app.get("/info")
async def info(settings: Annotated[config.Settings, Depends(get_settings)]):
return {
"app_name": settings.app_name,
"admin_email": settings.admin_email,
"items_per_user": settings.items_per_user,
}
ヒント
可能であれば、Annotated
バージョンを使用することをお勧めします。
from functools import lru_cache
from fastapi import Depends, FastAPI
from . import config
app = FastAPI()
@lru_cache
def get_settings():
return config.Settings()
@app.get("/info")
async def info(settings: config.Settings = Depends(get_settings)):
return {
"app_name": settings.app_name,
"admin_email": settings.admin_email,
"items_per_user": settings.items_per_user,
}
次に、次のリクエストの依存関係でget_settings()
を後続で呼び出すと、get_settings()
の内部コードを実行して新しいSettings
オブジェクトを作成する代わりに、最初の呼び出しで返された同じオブジェクトを繰り返し返します。
lru_cache
の技術的な詳細¶
@lru_cache
は、関数を再度計算する代わりに、最初に返された値を返すように、デコレートする関数を変更します。毎回関数のコードを実行します。
したがって、その下の関数は、引数の組み合わせごとに一度実行されます。そして、それらの引数の組み合わせのそれぞれによって返された値は、関数がまったく同じ引数の組み合わせで呼び出されるたびに繰り返し使用されます。
たとえば、次のような関数がある場合
@lru_cache
def say_hi(name: str, salutation: str = "Ms."):
return f"Hello {salutation} {name}"
プログラムは次のように実行される可能性があります。
sequenceDiagram
participant code as Code
participant function as say_hi()
participant execute as Execute function
rect rgba(0, 255, 0, .1)
code ->> function: say_hi(name="Camila")
function ->> execute: execute function code
execute ->> code: return the result
end
rect rgba(0, 255, 255, .1)
code ->> function: say_hi(name="Camila")
function ->> code: return stored result
end
rect rgba(0, 255, 0, .1)
code ->> function: say_hi(name="Rick")
function ->> execute: execute function code
execute ->> code: return the result
end
rect rgba(0, 255, 0, .1)
code ->> function: say_hi(name="Rick", salutation="Mr.")
function ->> execute: execute function code
execute ->> code: return the result
end
rect rgba(0, 255, 255, .1)
code ->> function: say_hi(name="Rick")
function ->> code: return stored result
end
rect rgba(0, 255, 255, .1)
code ->> function: say_hi(name="Camila")
function ->> code: return stored result
end
依存関係get_settings()
の場合、関数は引数をまったく受け取らないため、常に同じ値を返します。
このように、それはグローバル変数のように振る舞います。しかし、依存関数を使用しているので、テストのために簡単にオーバーライドできます。
@lru_cache
はPythonの標準ライブラリの一部であるfunctools
の一部です。詳しくはPython docs for @lru_cache
をご覧ください。
要約¶
Pydantic Settingsを使用して、Pydanticモデルのすべての機能を備えたアプリケーションの設定または構成を処理できます。
- 依存関係を使用することで、テストを簡素化できます。
.env
ファイルを使用できます。@lru_cache
を使用すると、テスト中にオーバーライドしながら、各リクエストに対してdotenvファイルを繰り返し読み取るのを回避できます。