🔦

Pydanticがわからないのでメモる2

2024/05/01に公開

概要

前回のpydanticのメモでおおよそ使い方に困らないだろうと思っていたら舌の根も乾かぬうちに実装方法がわからないバリデーションパターンに遭遇した。フィールド間の関係性に関する制約はどう設定するのだろうか?

対象クラスと設定したいルール

2つのboolフィールドを持つクラス

class Flags(BaseModel):
    is_flip: bool = True
    is_zoom: bool = True

すべてのフィールドがFalseの場合ValueError

2つの属性を持つFlagsのオブジェクトを作成する際に少なくとも1つはTrueでなくてはならない、というルールを設定したい。

is_flip is_zoom Validation result
False False raise ValueError
False True OK
True False OK
True True OK

pydantic.model_validatorを使う

https://github.com/rifumi/pydantic_my_tutorial/blob/main/how_to_use_model_validator.py#L8-L16

フィールドは特に手を加える必要なく、バリデーション関数を追加するのみでルールの追加は完了。

ところでfield_validatorやその他フィールド単位のルールが設定されている場合だと、model_validatorアノテーションが付加された関数はいつ動作するのか?

field_validatorとmodel_validatorの呼び出し順

確認用コード
https://github.com/rifumi/pydantic_my_tutorial/blob/main/order_validators.py#L1-L19

動作結果

fv() v:is_flip
fv() v:is_zoom
validatorf2d() called.
f2d:is_flip=True is_zoom=True

呼び出し順は

  1. is_flipのfield_validator
  2. is_zoomのfield_validator
  3. model_validator

という結果になった。
フィールドの確認順序はコンストラクタで指定した順番だろうか?という疑問を挙げてみたもののフィールドのバリデーション実行順番が問題になることはない(問題になるようなバリデーションはmodel_validatorで処理するべきでは?)と思うのでスルーする。
また、model_validatorのmode='before'は公式ドキュメントにBecause of this mode='before' validators are extremely flexible and powerful but can be cumbersome and error prone to implement.[1]と記載されており、この意見に同意のため必要となる特殊な状況下に遭遇しない限りは使わないことにする。

model_validatorによる動作確認

https://github.com/rifumi/pydantic_my_tutorial/blob/main/how_to_use_model_validator.py#L18-L45

結果

....
----------------------------------------------------------------------
Ran 4 tests in 0.001s

OK

前回記事

https://zenn.dev/slowman/articles/b59e265ebcb517

参考

https://docs.pydantic.dev/latest/concepts/validators/#field-validators
https://docs.pydantic.dev/latest/api/pydantic_core_schema/#pydantic_core.core_schema.ValidationInfo.field_name

脚注
  1. https://docs.pydantic.dev/latest/concepts/validators/#model-validators ↩︎

Discussion