概要
fastapi の BaseModel を使ってリクエストモデルを定義している場合に field_validator は自動でコールされます
ただ context (FieldValidationInfo) を使った場合には context が必ず None になってしまうので fastapi では context が使えません
今回は fastapi で context を使ったバリデーションを実現する方法を考えます
環境
- Python 3.11.3
- fastapi 0.100.1
- pydantic 2.1.1
サンプルコード
from fastapi import FastAPI
from pydantic import BaseModel, Field, FieldValidationInfo, field_validator
app = FastAPI()
class User(BaseModel):
name: str = Field()
age: int = Field()
@field_validator("name")
@classmethod
def validate_name(cls, v: str, info: FieldValidationInfo):
if info.context is not None:
# context がある場合のバリデーションは無視する
return v
print("validate_name")
if v not in ["hawksnowlog"]:
raise ValueError()
return v
@field_validator("age")
@classmethod
def validate_age_with_context(cls, v: int, info: FieldValidationInfo):
if info.context is None:
# context がない場合のバリデーションは無視する
return v
if "age_limit" not in info.context or not isinstance(
info.context["age_limit"], int
):
# context の値をチェック、正しくセットされていない場合はエラー
raise ValueError("info.context.age_limit が正しく設定されていません")
print("validate_age_with_context")
limit = int(info.context["age_limit"])
if v > limit:
raise ValueError(f"age が limit={limit} を超えています")
return v
@app.post("/")
async def test_context(user: User):
# ルーティングで一度 User のバリデーションをコールする
user_dict = user.model_dump()
# ここで context を付与して再度バリデーションをコールする
user = User.model_validate(user_dict, context={"age_limit": 9})
return user
ちょっと解説
ルーティングのリクエストモデルを受け取るところで一度各種バリデーションがコールされます
そしてルーティング内で model_validate することで再度バリデーションをコールします
そしてここで context を付与してコールします
バリデーション側では context が設定されているかどうかでバリデーションを実行するか判断しています
context が不要なバリデーションを二度コールしても一度通っているので必ず成功するため実行しても問題ないですが処理効率的に二回実行しても意味がないのでスルーするようにしています
最後に
これ以外に context ありで fastapi からバリデーションを実行する方法があるのだろうか
0 件のコメント:
コメントを投稿