厳格モード
??? 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 の使用中に厳密モードの検証を取得するにはさまざまな方法がありますが、これについては以下で詳しく説明します。
- JSON の
BaseModel.model_validate、TypeAdapter.validate_pythonなどの検証メソッドにstrict=True渡す BaseModel、dataclass、またはTypedDictのフィールドでField(strict=True)を使用する- フィールドの型アノテーションとして
pydantic.types.Strict使用する- Pydantic は、
pydantic.types.StrictIntなど、すでにStrictの注釈が付けられているいくつかの型エイリアスを提供します。
- Pydantic は、
ConfigDict(strict=True)の使用
厳密モードでの型強制¶
ほとんどの型では、厳密モードで 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_jsonとBaseModel.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_configでConfigDict(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_configでstrict=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]
"""
本文总阅读量次