👹

Pydantic で追加のフィールドを禁止する

に公開
1

Pydantic v2でのやり方

結論から言うとこんなふうにやります。

from pydantic import BaseModel, ConfigDict, ValidationError


class Model(BaseModel):
    x: str

    model_config = ConfigDict(extra='forbid')


try:
    Model(x='test', y='test')
except ValidationError as exc:
    print(repr(exc.errors()[0]['type']))
    #> 'extra_forbidden'

この通り、ConfigDict(extra='forbid')により、追加のフィールドを無効化することができ、タイポを抑制できます。APIに無効な入力が与えられた時にエラーが吐けるので便利です。
JSON Schema で言うところの additionalProperty: false
ですね。
プログラムの健全性を保つ上で、通常、設定すべき項目だと思いますが、フィールドが多く、何度も利用するクラスの場合、バリデーションが重くなることがあるので注意です。

参考:
https://docs.pydantic.dev/latest/errors/validation_errors/#extra_forbidden

以下はPydantic v1時代の古い情報となりますが、当時の雰囲気を知るための歴史的資料(傲慢)になると信じて残しておきます。

結論から

from pydantic import BaseModel, ValidationError, Extra


class Model(BaseModel, extra=Extra.forbid):
    a: str


try:
    Model(a='spam', b='oh no')
except ValidationError as e:
    print(e)
    """
    1 validation error for Model
    b
      extra fields not permitted (type=value_error.extra)
    """

この通り、Extra.forbidにより、追加のフィールドを無効化することができ、タイポを抑制できます。APIに無効な入力が与えられた時にエラーが吐けるので便利です。

JSON Schema で言うところの additionalProperty: false
ですね。

プログラムの健全性を保つ上で、通常、設定すべき項目だと思いますが、フィールドが多く、何度も利用するクラスの場合、バリデーションが重くなることがあるので注意です。

参考

https://pydantic-docs.helpmanual.io/usage/model_config/

(おまけ) バリデーションが重いので実際の実装を見に行く

大量のフィールドが存在するクラスにExtra.forbidを指定したときに、バリデーションが著しく遅くなったため、そのパフォーマンス低下の原因を調査しました。

https://github.com/samuelcolvin/pydantic/blob/87da9ac23f0917741c0536b474cf13cdbed50dfc/pydantic/main.py#L1030-L1042

ココらへんが遅いことはわかりました。setの生成・処理が遅いのでしょうか。

私はFastAPIの内部でPydanticを使っていました。非同期処理が得意なASGI (Asynchronous Server Gateway Interface、uvloopがCythonで実装されている) による実装で高速化されているFastAPIと比較してpure Pythonによるフィールドチェックがボトルネックになっていたと考えられます。今後Rust実装で、フィールドチェックを高速化するらしいので、それが楽しみです!
https://pydantic-docs.helpmanual.io/blog/pydantic-v2/

(追記) Alpha 版がリリースされました!

https://docs.pydantic.dev/latest/blog/pydantic-v2-alpha/

Discussion