pydanticのバリデーションで簡単にバリデーションが出来るようにデコレータが提供されている。
@field_validatorデコレータは、モデルの特定のフィールドに対して検証をしたい場合に使用する。
@model_validatorデコレータは、モデルのデータ全体に対して検証をしたい場合に使用する。
フィールドだけで完結するバリデーションはfield_validatorデコレータを使ったほうがいい。 モデルのフィールド同士の組み合わせでバリデーションをしたい場合はmodel_validatorデコレータを使用すると良い。
肉とソースの組み合わせでカルビに合わないソースが来たら、エラーにするモデルを作るとこんな感じ。
from pydantic import BaseModel, model_validator from typing import Any class Yakiniku(BaseModel): beaf: str source: str @model_validator(mode="after") def check(self): if self.beaf == "カルビ": if self.source == "タレ" or self.source == "レモン": return self else: raise ValueError("おかしな組み合わせ") raise ValueError("カルビ以外嫌い") print(Yakiniku(beaf="カルビ", source="レモン")) print(Yakiniku(beaf="カルビ", source="タレ")) print(Yakiniku(beaf="カルビ", source="チョコ"))
カルビとレモンまたはカルビとタレの組み合わせであれば、インスタンスは生成可能。 それ以外の場合、ValidationErrorになる。
beaf='カルビ' source='レモン' beaf='カルビ' source='タレ' Traceback (most recent call last): File "/Users/kotaro/github/pythonStudy/src/pydantic/model_fileld.py", line 21, in <module> print(Yakiniku(beaf="カルビ", source="チョコ")) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/Users/kotaro/.pyenv/versions/3.12.1/lib/python3.12/site-packages/pydantic/main.py", line 164, in __init__ __pydantic_self__.__pydantic_validator__.validate_python(data, self_instance=__pydantic_self__) pydantic_core._pydantic_core.ValidationError: 1 validation error for Yakiniku Value error, おかしな組み合わせ [type=value_error, input_value={'beaf': 'カルビ', 'source': 'チョコ'}, input_type=dict] For further information visit https://errors.pydantic.dev/2.5/v/value_error
型エイリアスを使用して、バリデーションを実現する方法もある。 Annotatedに、AfterValidatorまたはBaforeValidatorに検証で使用する関数を指定すればよい。
from typing import Optional, Any, Annotated from pydantic.functional_validators import AfterValidator, BeforeValidator def plus(v: Any) -> Any: print("Call plus function") return v + 1 def double(v: Any) -> Any: print("Call dobule function") return v * 2 MyNumber = Annotated[int, AfterValidator(double), BeforeValidator(plus)] class DemoModel(BaseModel): number: MyNumber print(DemoModel(2))
Before だと実際のデータに対するチェックができる。
上記のソースの場合、DemoModelのパラメータに"a"を指定するとplus関数には"a"が渡される。
return文で v + 1
しているので、TypeErrorが発生する。
Afterだとpydanticでの型チェック後にバリデーションが実行される。 そのため、上記のソースの場合、DemoModelはint型を期待しているため、dobule関数を実行する前に 型違反でValidationErrorが発生することになる。
model_validatorデコレータは、modeでbefore/afterが指定できる。 その際に、beforeを指定するとクラスメソッドとして定義が必要なので、@classmothodを忘れずに
さいごに
フィールドやモデル単位でバリデーションが設定出来るので、データクラスを使用する側で違反していないかチェックする必要はない。そのため、データクラスを使用する側と使用される側で正しい責務に分けれる。
(そもそも、コンストラクタ内でチェックするから責務が分かれるのか・・・)
コンストラクタでチェックするにも処理が長くなるので、関数が分けれることもメリットだと感じた。
modeにはplainとwrapがあるけど、どんなものかはいつか調べる。