コンテンツにスキップ

Strict Mode

API Documentation

pydantic.types.Strict

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

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

このユースケースをよりよくサポートするために、Pydanticは、モデルごと、フィールドごと、または検証呼び出しごとに有効にできる"strictモード"を提供している。strictモードが有効になると、Pydanticはデータを強制する際の甘さを大幅に減らし、データが正しいタイプでない場合はエラーを発生させる。

次に、strictモードとdefault/"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を使用しながらstrictモードの検証を行うには、さまざまな方法があります。これについては、以下で詳しく説明します。

Type coercions in strict mode

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

strictモードでJSONからのデータを検証する場合は、より緩いことに注意してください。例えば、UUIDフィールドを検証する場合、strのインスタンスはJSONからの検証では受け入れられますが、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モードで入力として許可されるタイプの詳細については、Conversion Tableを参照してください。

Strict mode in method calls

これまでに紹介したすべての例では、検証メソッドへのキーワード引数としてstrict=Trueを使用することで、strictモードの検証を行います。これは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]
    """

Strict mode with Field

モデルの個々のフィールドに対しては、[フィールドにstrict=Trueを設定する]ことができます(./api/fields.md#pydantic.fields.Field)。 これにより、検証メソッドが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]
    """

Using Field as an annotation

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]
    """

Strict mode with Annotated[..., Strict()]

API Documentation

pydantic.types.Strict

PydanticはStrictクラスも提供しており、これは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が提供する厳密な初期状態の型を実装するために使用されるメソッドです。

Strict mode with ConfigDict

BaseModel

複雑な入力タイプのすべてのフィールドに対してstrictモードを有効にしたい場合は、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を設定することで、個々のフィールドの厳密さを上書きできます。

from pydantic import BaseModel, ConfigDict, Field


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

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

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

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にも当てはまります)

これが望ましくない場合は、関係するすべてのタイプに対してstrictモードが有効になっていることを確認してください。 たとえば、モデルクラスに対してこれを行うには、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]
    """

Dataclasses and TypedDict

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

可能であれば、pydantic.types.Strictannotationでフィールドに注釈を付けることで、バニラデータクラスやTypedDictサブクラスに対してネストされたstrictモードを実現できます。

しかし、これが不可能な場合(例えば、サードパーティの型を扱う場合)は、その型に__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キーワード引数を使用してstrictモードを取得することもできます。

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モードは、@validate_callデコレータでconfigキーワード引数を渡すことによっても使用できます。

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]
    """