コンテンツにスキップ

厳格モード

??? API「APIドキュメント」 pydantic.types.Strict

デフォルトでは、Pydantic は可能な場合、値を目的の型に強制しようとします。たとえば、文字列"123"入力としてintフィールドに渡すと、それは123に変換されます。この強制動作は、UUID、URL パラメーター、HTTP ヘッダー、環境変数、ユーザー入力などの多くのシナリオで役立ちます。

ただし、これが望ましくなく、データを強制するのではなく Pydantic にエラーを発生させたい状況もあります。

このユースケースをより適切にサポートするために、Pydantic はモデルごと、フィールドごと、さらには検証呼び出しごとに有効にできる「厳密モード」を提供します。厳密モードが有効になっている場合、Pydantic はデータを強制する際の寛容性が大幅に低下し、データが正しい型でない場合はエラーが発生します。

以下は、strict モードとデフォルト/「lax」モードでの検証動作の違いを示す簡単な例です。

from pydantic import BaseModel, ValidationError


class MyModel(BaseModel):
    x: int


print(MyModel.model_validate({'x': '123'}))  # lax mode
#> x=123

try:
    MyModel.model_validate({'x': '123'}, strict=True)  # strict mode
except ValidationError as exc:
    print(exc)
    """
    1 validation error for MyModel
    x
      Input should be a valid integer [type=int_type, input_value='123', input_type=str]
    """

Pydantic の使用中に厳密モードの検証を取得するにはさまざまな方法がありますが、これについては以下で詳しく説明します。

厳密モードでの型強制

ほとんどの型では、厳密モードで Python からのデータを検証する場合、正確な型のインスタンスのみが受け入れられます。たとえば、 intフィールドを検証する場合、 intのインスタンスのみが受け入れられます。 floatまたはstrのインスタンスを渡すと、 ValidationErrorが発生します。

厳密モードで JSON からのデータを検証する場合はより緩やかになることに注意してください。たとえば、 UUIDフィールドを検証する場合、JSON から検証する場合はstrのインスタンスが受け入れられますが、Python からは受け入れられません。

import json
from uuid import UUID

from pydantic import BaseModel, ValidationError


class MyModel(BaseModel):
    guid: UUID


data = {'guid': '12345678-1234-1234-1234-123456789012'}

print(MyModel.model_validate(data))  # OK: lax
#> guid=UUID('12345678-1234-1234-1234-123456789012')

print(
    MyModel.model_validate_json(json.dumps(data), strict=True)
)  # OK: strict, but from json
#> guid=UUID('12345678-1234-1234-1234-123456789012')

try:
    MyModel.model_validate(data, strict=True)  # Not OK: strict, from python
except ValidationError as exc:
    print(exc.errors(include_url=False))
    """
    [
        {
            'type': 'is_instance_of',
            'loc': ('guid',),
            'msg': 'Input should be an instance of UUID',
            'input': '12345678-1234-1234-1234-123456789012',
            'ctx': {'class': 'UUID'},
        }
    ]
    """

厳密モードで入力として許可される型の詳細については、 「変換テーブル」を参照してください。

メソッド呼び出しの厳密モード

これまでに含まれているすべての例では、検証メソッドのキーワード引数としてstrict=Trueを使用することで、厳密モードの検証が行われます。これはBaseModel.model_validateについて示しましたが、これはTypeAdapterを使用することで任意の型でも機能します。

from pydantic import TypeAdapter, ValidationError

print(TypeAdapter(bool).validate_python('yes'))  # OK: lax
#> True

try:
    TypeAdapter(bool).validate_python('yes', strict=True)  # Not OK: strict
except ValidationError as exc:
    print(exc)
    """
    1 validation error for bool
      Input should be a valid boolean [type=bool_type, input_value='yes', input_type=str]
    """

これは、 TypeAdapterでより「複雑な」型を使用している場合でも機能することに注意してください。

from dataclasses import dataclass

from pydantic import TypeAdapter, ValidationError


@dataclass
class MyDataclass:
    x: int


try:
    TypeAdapter(MyDataclass).validate_python({'x': '123'}, strict=True)
except ValidationError as exc:
    print(exc)
    """
    1 validation error for MyDataclass
      Input should be an instance of MyDataclass [type=dataclass_exact_type, input_value={'x': '123'}, input_type=dict]
    """

これは、 TypeAdapter.validate_jsonBaseModel.model_validate_jsonメソッドでも機能します。

import json
from typing import List
from uuid import UUID

from pydantic import BaseModel, TypeAdapter, ValidationError

try:
    TypeAdapter(List[int]).validate_json('["1", 2, "3"]', strict=True)
except ValidationError as exc:
    print(exc)
    """
    2 validation errors for list[int]
    0
      Input should be a valid integer [type=int_type, input_value='1', input_type=str]
    2
      Input should be a valid integer [type=int_type, input_value='3', input_type=str]
    """


class Model(BaseModel):
    x: int
    y: UUID


data = {'x': '1', 'y': '12345678-1234-1234-1234-123456789012'}
try:
    Model.model_validate(data, strict=True)
except ValidationError as exc:
    # Neither x nor y are valid in strict mode from python:
    print(exc)
    """
    2 validation errors for Model
    x
      Input should be a valid integer [type=int_type, input_value='1', input_type=str]
    y
      Input should be an instance of UUID [type=is_instance_of, input_value='12345678-1234-1234-1234-123456789012', input_type=str]
    """

json_data = json.dumps(data)
try:
    Model.model_validate_json(json_data, strict=True)
except ValidationError as exc:
    # From JSON, x is still not valid in strict mode, but y is:
    print(exc)
    """
    1 validation error for Model
    x
      Input should be a valid integer [type=int_type, input_value='1', input_type=str]
    """

Field付きストリクトモード

モデルの個々のフィールドについては、そのフィールドにstrict=Trueを設定できます。これにより、 strict=Trueを指定せずに検証メソッドが呼び出された場合でも、そのフィールドに対して strict モードの検証が使用されます。

strict=Trueが設定されているフィールドのみが影響を受けます。

from pydantic import BaseModel, Field, ValidationError


class User(BaseModel):
    name: str
    age: int
    n_pets: int


user = User(name='John', age='42', n_pets='1')
print(user)
#> name='John' age=42 n_pets=1


class AnotherUser(BaseModel):
    name: str
    age: int = Field(strict=True)
    n_pets: int


try:
    anotheruser = AnotherUser(name='John', age='42', n_pets='1')
except ValidationError as e:
    print(e)
    """
    1 validation error for AnotherUser
    age
      Input should be a valid integer [type=int_type, input_value='42', input_type=str]
    """

フィールドを厳密にすると、モデル クラスをインスタンス化するときに実行される検証にも影響することに注意してください。

from pydantic import BaseModel, Field, ValidationError


class Model(BaseModel):
    x: int = Field(strict=True)
    y: int = Field(strict=False)


try:
    Model(x='1', y='2')
except ValidationError as exc:
    print(exc)
    """
    1 validation error for Model
    x
      Input should be a valid integer [type=int_type, input_value='1', input_type=str]
    """

Field注釈として使用する

Field(strict=True) (または他のキーワード引数とともに) は、必要に応じて注釈として使用できることに注意してください。たとえば、 TypedDictを使用する場合です。

from typing_extensions import Annotated, TypedDict

from pydantic import Field, TypeAdapter, ValidationError


class MyDict(TypedDict):
    x: Annotated[int, Field(strict=True)]


try:
    TypeAdapter(MyDict).validate_python({'x': '1'})
except ValidationError as exc:
    print(exc)
    """
    1 validation error for typed-dict
    x
      Input should be a valid integer [type=int_type, input_value='1', input_type=str]
    """

Annotated[..., Strict()]を使用した Strict モード

??? API「APIドキュメント」 pydantic.types.Strict

Pydantic は、typing.Annotated クラスのメタデータとして使用することを目的としたStrictクラスも提供します。この注釈は、注釈付きフィールドが厳密モードで検証される必要があることを示します。

from typing_extensions import Annotated

from pydantic import BaseModel, Strict, ValidationError


class User(BaseModel):
    name: str
    age: int
    is_active: Annotated[bool, Strict()]


User(name='David', age=33, is_active=True)
try:
    User(name='David', age=33, is_active='True')
except ValidationError as exc:
    print(exc)
    """
    1 validation error for User
    is_active
      Input should be a valid boolean [type=bool_type, input_value='True', input_type=str]
    """

実際、これは、 StrictIntなど、Pydantic が提供するいくつかのすぐに使える厳密な型を実装するために使用されるメソッドです。

ConfigDictを使用したストリクト モード

BaseModel

複合入力タイプのすべてのフィールドに対して厳密モードを有効にしたい場合は、 model_configConfigDict(strict=True)を使用できます。

from pydantic import BaseModel, ConfigDict, ValidationError


class User(BaseModel):
    model_config = ConfigDict(strict=True)

    name: str
    age: int
    is_active: bool


try:
    User(name='David', age='33', is_active='yes')
except ValidationError as exc:
    print(exc)
    """
    2 validation errors for User
    age
      Input should be a valid integer [type=int_type, input_value='33', input_type=str]
    is_active
      Input should be a valid boolean [type=bool_type, input_value='yes', input_type=str]
    """

!!! note モデルのmodel_configstrict=True使用する場合でも、個々のフィールドにstrict=Falseを設定することで、個々のフィールドの厳密性をオーバーライドできます。

```py
from pydantic import BaseModel, ConfigDict, Field


class User(BaseModel):
    model_config = ConfigDict(strict=True)

    name: str
    age: int = Field(strict=False)
```

厳密モードは、ネストされたモデル フィールドには再帰的に適用されないことに注意してください。

from pydantic import BaseModel, ConfigDict, ValidationError


class Inner(BaseModel):
    y: int


class Outer(BaseModel):
    model_config = ConfigDict(strict=True)

    x: int
    inner: Inner


print(Outer(x=1, inner=Inner(y='2')))
#> x=1 inner=Inner(y=2)

try:
    Outer(x='1', inner=Inner(y='2'))
except ValidationError as exc:
    print(exc)
    """
    1 validation error for Outer
    x
      Input should be a valid integer [type=int_type, input_value='1', input_type=str]
    """

(これはデータクラスとTypedDictにも当てはまります。)

これが望ましくない場合は、関係するすべてのタイプに対して厳密モードが有効になっていることを確認する必要があります。たとえば、これは、共有基本クラスを使用してモデル クラスに対して実行できます。 model_config = ConfigDict(strict=True) :

from pydantic import BaseModel, ConfigDict, ValidationError


class MyBaseModel(BaseModel):
    model_config = ConfigDict(strict=True)


class Inner(MyBaseModel):
    y: int


class Outer(MyBaseModel):
    x: int
    inner: Inner


try:
    Outer.model_validate({'x': 1, 'inner': {'y': '2'}})
except ValidationError as exc:
    print(exc)
    """
    1 validation error for Outer
    inner.y
      Input should be a valid integer [type=int_type, input_value='2', input_type=str]
    """

データクラスとTypedDict

Pydantic データクラスは、 BaseModelを使用した上記の例と同様に動作します。ただし、 model_configの代わりにconfigキーワード引数を使用する必要がある点が異なります。 @pydantic.dataclasses.dataclass デコレーター。

可能であれば、フィールドにpydantic.types.Strictアノテーションを付けることで、バニラ データクラスまたはTypedDictサブクラスのネストされた厳密モードを実現できます。

ただし、これが_不可能_な場合 (サードパーティの型を使用する場合など)、型の__pydantic_config__属性を設定することで、Pydantic がその型に使用する構成を設定できます。

from typing_extensions import TypedDict

from pydantic import ConfigDict, TypeAdapter, ValidationError


class Inner(TypedDict):
    y: int


Inner.__pydantic_config__ = ConfigDict(strict=True)


class Outer(TypedDict):
    x: int
    inner: Inner


adapter = TypeAdapter(Outer)
print(adapter.validate_python({'x': '1', 'inner': {'y': 2}}))
#> {'x': 1, 'inner': {'y': 2}}


try:
    adapter.validate_python({'x': '1', 'inner': {'y': '2'}})
except ValidationError as exc:
    print(exc)
    """
    1 validation error for typed-dict
    inner.y
      Input should be a valid integer [type=int_type, input_value='2', input_type=str]
    """

TypeAdapter

TypeAdapterクラスの config キーワード引数を使用して、厳密モードを取得することもできます。

from pydantic import ConfigDict, TypeAdapter, ValidationError

adapter = TypeAdapter(bool, config=ConfigDict(strict=True))

try:
    adapter.validate_python('yes')
except ValidationError as exc:
    print(exc)
    """
    1 validation error for bool
      Input should be a valid boolean [type=bool_type, input_value='yes', input_type=str]
    """

@validate_call

Strict モードは、 configキーワード引数を渡すことで@validate_callデコレータでも使用できます。

from pydantic import ConfigDict, ValidationError, validate_call


@validate_call(config=ConfigDict(strict=True))
def foo(x: int) -> int:
    return x


try:
    foo('1')
except ValidationError as exc:
    print(exc)
    """
    1 validation error for foo
    0
      Input should be a valid integer [type=int_type, input_value='1', input_type=str]
    """

本文总阅读量