严格模式
API 文档
默认情况下,Pydantic 会尝试在可能的情况下将值强制转换为所需的类型。例如,您可以将字符串 "123" 作为 int 字段的输入传递,它将被转换为 123。这种强制转换行为在许多场景中都很有用 — 例如:UUID、URL 参数、HTTP 标头、环境变量、用户输入等。
然而,在某些情况下,这是不希望发生的,您希望 Pydantic 报错而不是强制转换数据。
为了更好地支持这种用例,Pydantic 提供了“严格模式”,可以针对每个模型、每个字段甚至每次验证调用启用。启用严格模式后,Pydantic 在强制转换数据时会宽松度会降低,如果数据类型不正确,则会报错。
这是一个简短的示例,展示了严格模式和默认/"宽松"模式下验证行为的差异
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=True
传递给验证方法,例如BaseModel.model_validate
、TypeAdapter.validate_python
,以及 JSON 的类似方法 - 在
BaseModel
、dataclass
或TypedDict
的字段中使用Field(strict=True)
- 在字段上使用
pydantic.types.Strict
作为类型注解 - Pydantic 提供了一些已使用
Strict
注解的类型别名,例如pydantic.types.StrictInt
- 使用
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 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=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 import Annotated
from typing_extensions import 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 MyDict
x
Input should be a valid integer [type=int_type, input_value='1', input_type=str]
"""
from typing import Annotated
from typing import 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 MyDict
x
Input should be a valid integer [type=int_type, input_value='1', input_type=str]
"""
使用 Annotated[..., Strict()] 的严格模式¶
API 文档
Pydantic 还提供了 Strict
类,该类旨在与 typing.Annotated
类一起用作元数据;此注解表明应以严格模式验证带注解的字段
from typing 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]
"""
实际上,这是用于实现 Pydantic 提供的一些开箱即用的严格模式类型的方法,例如 StrictInt
。
使用 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]
"""
注意
当通过模型的 model_config
使用 strict=True
时,您仍然可以通过在单个字段上设置 strict=False
来覆盖单个字段的严格性
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 展示的示例,只是您应该使用 config
关键字参数来修饰 @pydantic.dataclasses.dataclass
,而不是 model_config
。
如果可能,您可以通过使用 pydantic.types.Strict
注解来注解字段,从而为普通的 dataclass 或 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 Outer
inner.y
Input should be a valid integer [type=int_type, input_value='2', input_type=str]
"""
from typing 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 Outer
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
¶
严格模式也适用于 @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]
"""