迁移指南
Pydantic V2 引入了许多 API 更改,包括一些破坏性更改。
此页面提供指南,重点介绍最重要的更改,以帮助您将代码从 Pydantic V1 迁移到 Pydantic V2。
安装 Pydantic V2¶
Pydantic V2 现在是 Pydantic 的当前生产版本。您可以从 PyPI 安装 Pydantic V2
pip install -U pydantic
如果您遇到任何问题,请在 GitHub 中创建 issue,并使用 bug V2
标签。这将帮助我们积极监控和跟踪错误,并继续提高库的性能。
如果您出于任何原因需要使用最新的 Pydantic V1,请参阅下面的 继续使用 Pydantic V1 功能 部分,了解有关从 pydantic.v1
安装和导入的详细信息。
代码转换工具¶
我们创建了一个工具来帮助您迁移代码。此工具仍处于 beta 阶段,但我们希望它能帮助您更快地迁移代码。
您可以从 PyPI 安装该工具
pip install bump-pydantic
用法很简单。如果您的项目结构是
* repo_folder
* my_package
* <python source files> ...
那么您需要执行
cd /path/to/repo_folder
bump-pydantic my_package
有关更多信息,请参阅 Bump Pydantic 仓库。
继续使用 Pydantic V1 功能¶
当您需要时,Pydantic V1 仍然可用,但我们建议迁移到 Pydantic V2 以获得其改进和新功能。
如果您需要使用最新的 Pydantic V1,可以使用以下命令安装它
pip install "pydantic==1.*"
Pydantic V2 包还继续通过从 pydantic.v1
导入来提供对 Pydantic V1 API 的访问。
例如,您可以使用 Pydantic V1 中的 BaseModel
类,而不是 Pydantic V2 的 pydantic.BaseModel
类
from pydantic.v1 import BaseModel
您还可以导入已从 Pydantic V2 中删除的函数,例如 lenient_isinstance
from pydantic.v1.utils import lenient_isinstance
Pydantic V1 文档可在 https://docs.pydantic.org.cn/1.10/ 找到。
在 v1/v2 环境中使用 Pydantic v1 功能¶
从 pydantic>=1.10.17
开始,pydantic.v1
命名空间可以在 V1 中使用。这使得迁移到 V2 更容易,V2 也支持 pydantic.v1
命名空间。为了解除 pydantic<2
依赖项并继续使用 V1 功能,请执行以下步骤
- 将
pydantic<2
替换为pydantic>=1.10.17
- 查找并替换所有出现的
from pydantic.<module> import <object>
替换为
from pydantic.v1.<module> import <object>
以下是如何根据您的 pydantic
版本导入 pydantic
的 v1 功能
从 v1.10.17
开始,.v1
命名空间在 V1 中可用,允许如下导入
from pydantic.v1.fields import ModelField
所有版本的 Pydantic V1 和 V2 都支持以下导入模式,以防您不知道您正在使用的 Pydantic 版本
try:
from pydantic.v1.fields import ModelField
except ImportError:
from pydantic.fields import ModelField
注意
当使用 pydantic>=1.10.17,<2
和 .v1
命名空间导入模块时,这些模块将不是与不带 .v1
命名空间的相同导入的相同模块,但导入的符号将是。例如,pydantic.v1.fields is not pydantic.fields
,但 pydantic.v1.fields.ModelField is pydantic.fields.ModelField
。幸运的是,这在绝大多数情况下不太可能相关。这只是为了提供更平滑的迁移体验而带来的不幸后果。
迁移指南¶
以下部分详细介绍了 Pydantic V2 中最重要的更改。
pydantic.BaseModel
的更改¶
各种方法名称已更改;所有未弃用的 BaseModel
方法现在都具有与格式 model_.*
或 __.*pydantic.*__
匹配的名称。在可能的情况下,我们保留了已弃用的方法及其旧名称,以帮助简化迁移,但调用它们将发出 DeprecationWarning
。
Pydantic V1 | Pydantic V2 |
---|---|
__fields__ |
model_fields |
__private_attributes__ |
__pydantic_private__ |
__validators__ |
__pydantic_validator__ |
construct() |
model_construct() |
copy() |
model_copy() |
dict() |
model_dump() |
json_schema() |
model_json_schema() |
json() |
model_dump_json() |
parse_obj() |
model_validate() |
update_forward_refs() |
model_rebuild() |
- 一些内置的数据加载功能已被计划移除。特别是,
parse_raw
和parse_file
现在已弃用。在 Pydantic V2 中,model_validate_json
的工作方式类似于parse_raw
。否则,您应该加载数据,然后将其传递给model_validate
。 from_orm
方法已被弃用;您现在只需使用model_validate
(等效于 Pydantic V1 中的parse_obj
)即可实现类似的功能,只要您在模型配置中设置了from_attributes=True
。- 模型的
__eq__
方法已更改。- 模型只能与其他
BaseModel
实例相等。 - 要使两个模型实例相等,它们必须具有相同的
- 类型(或者,在泛型模型的情况下,非参数化的泛型原始类型)
- 字段值
- 额外值(仅当
model_config['extra'] == 'allow'
时相关) - 私有属性值;具有不同私有属性值的模型不再相等。
- 模型不再等于包含其数据的 dict。
- 不同类型的非泛型模型永远不相等。
- 具有不同原始类型的泛型模型永远不相等。我们不要求精确的类型相等性,以便例如,
MyGenericModel[Any]
的实例可以等于MyGenericModel[int]
的实例。
- 模型只能与其他
- 我们已用名为
RootModel
的新类型替换了使用__root__
字段来指定“自定义根模型”,该类型旨在替换 Pydantic V1 中名为__root__
的字段的功能。请注意,RootModel
类型不再支持arbitrary_types_allowed
配置设置。有关解释,请参阅 此 issue 评论。 - 我们已显着扩展了 Pydantic 在自定义序列化方面的能力。特别是,我们添加了
@field_serializer
、@model_serializer
和@computed_field
装饰器,它们各自解决了 Pydantic V1 中的各种缺点。- 有关这些新装饰器的用法文档,请参阅 自定义序列化器。
- 由于性能开销和实现复杂性,我们现在已弃用在模型配置中指定
json_encoders
的支持。此功能最初是为了实现自定义序列化逻辑而添加的,我们认为在大多数常见情况下,新的序列化装饰器是更好的选择。
- 当模型的子类作为父模型中的嵌套字段出现时,我们更改了与序列化模型子类相关的行为。在 V1 中,我们总是包含子类实例中的所有字段。在 V2 中,当我们转储模型时,我们只包含在字段的注解类型上定义的字段。这有助于防止一些意外的安全漏洞。您可以在模型导出文档的 BaseModel、dataclasses、TypedDict 字段的子类实例 部分阅读有关此内容的更多信息(包括如何选择退出此行为)。
GetterDict
已被移除,因为它只是已移除的orm_mode
的实现细节。- 在许多情况下,传递给构造函数的参数将被复制,以便执行验证,并在必要时进行强制转换。在将可变对象作为参数传递给构造函数的情况下,这一点尤为重要。您可以在 此处 查看示例 + 更多详细信息。
.json()
方法已弃用,尝试使用带有indent
或ensure_ascii
等参数的已弃用方法可能会导致令人困惑的错误。为了获得最佳结果,请切换到 V2 的等效方法model_dump_json()
。如果您仍然想使用所述参数,可以使用 此解决方法。- 非字符串键值的 JSON 序列化通常使用
str(key)
完成,这导致了一些行为上的变化,例如以下情况
from typing import Optional
from pydantic import BaseModel as V2BaseModel
from pydantic.v1 import BaseModel as V1BaseModel
class V1Model(V1BaseModel):
a: dict[Optional[str], int]
class V2Model(V2BaseModel):
a: dict[Optional[str], int]
v1_model = V1Model(a={None: 123})
v2_model = V2Model(a={None: 123})
# V1
print(v1_model.json())
#> {"a": {"null": 123}}
# V2
print(v2_model.model_dump_json())
#> {"a":{"None":123}}
model_dump_json()
结果被压缩以节省空间,并且并不总是与json.dumps()
输出完全匹配。话虽如此,您可以轻松修改json.dumps()
结果中使用的分隔符,以使两个输出对齐
import json
from pydantic import BaseModel as V2BaseModel
from pydantic.v1 import BaseModel as V1BaseModel
class V1Model(V1BaseModel):
a: list[str]
class V2Model(V2BaseModel):
a: list[str]
v1_model = V1Model(a=['fancy', 'sushi'])
v2_model = V2Model(a=['fancy', 'sushi'])
# V1
print(v1_model.json())
#> {"a": ["fancy", "sushi"]}
# V2
print(v2_model.model_dump_json())
#> {"a":["fancy","sushi"]}
# Plain json.dumps
print(json.dumps(v2_model.model_dump()))
#> {"a": ["fancy", "sushi"]}
# Modified json.dumps
print(json.dumps(v2_model.model_dump(), separators=(',', ':')))
#> {"a":["fancy","sushi"]}
pydantic.generics.GenericModel
的更改¶
pydantic.generics.GenericModel
类不再必要,并且已被删除。相反,您现在可以通过直接在 BaseModel
子类上添加 Generic
作为父类来创建泛型 BaseModel
子类。这看起来像 class MyGenericModel(BaseModel, Generic[T]): ...
。
不支持混合使用 V1 和 V2 模型,这意味着此类泛型 BaseModel
(V2) 的类型参数不能是 V1 模型。
虽然它可能不会引发错误,但我们强烈建议不要在 isinstance
检查中使用参数化泛型。
- 例如,您不应该执行
isinstance(my_model, MyGenericModel[int])
。但是,可以执行isinstance(my_model, MyGenericModel)
。(请注意,对于标准泛型,使用参数化泛型进行子类检查会引发错误。) - 如果您需要对参数化泛型执行
isinstance
检查,可以通过子类化参数化泛型类来完成。这看起来像class MyIntModel(MyGenericModel[int]): ...
和isinstance(my_model, MyIntModel)
。
在 泛型模型 文档中查找更多信息。
pydantic.Field
的更改¶
Field
不再支持将任意关键字参数添加到 JSON schema。相反,您要添加到 JSON schema 的任何额外数据都应作为字典传递给 json_schema_extra
关键字参数。
在 Pydantic V1 中,当未设置别名时,alias
属性返回字段的名称。在 Pydantic V2 中,此行为已更改为在未设置别名时返回 None
。
以下属性已从 Field
中删除或更改
const
min_items
(请改用min_length
)max_items
(请改用max_length
)unique_items
allow_mutation
(请改用frozen
)regex
(请改用pattern
)final
(请改用 typing.Final 类型提示)
字段约束不再自动向下推送到泛型的参数。例如,您不能再通过提供 my_list: list[str] = Field(pattern=".*")
来验证列表的每个元素是否与正则表达式匹配。相反,请使用 typing.Annotated
在 str
本身提供注解:my_list: list[Annotated[str, Field(pattern=".*")]]
dataclasses 的更改¶
Pydantic dataclasses 继续用于在标准 dataclasses 上启用数据验证,而无需继承 BaseModel
子类。Pydantic V2 对此 dataclass 行为引入了以下更改
- 当用作字段时,dataclasses(Pydantic 或 vanilla)不再接受元组作为验证输入;应改用 dict。
- Pydantic dataclasses 中的
__post_init__
现在将在验证之后而不是之前调用。- 因此,
__post_init_post_parse__
方法将变得冗余,因此已被删除。
- 因此,
- Pydantic 不再支持 Pydantic dataclasses 的
extra='allow'
,其中传递给初始化程序的额外字段将作为 dataclass 上的额外属性存储。extra='ignore'
仍然受支持,用于在解析数据时忽略意外字段,它们只是不会存储在实例上。 - Pydantic dataclasses 不再具有属性
__pydantic_model__
,并且不再使用底层BaseModel
来执行验证或提供其他功能。- 要执行验证、生成 JSON schema 或使用 V1 中可能需要
__pydantic_model__
的任何其他功能,您现在应该使用TypeAdapter
(下面将详细讨论)包装 dataclass 并使用其方法。
- 要执行验证、生成 JSON schema 或使用 V1 中可能需要
- 在 Pydantic V1 中,如果您使用 vanilla(即非 Pydantic)dataclass 作为字段,则父类型的配置将像dataclass 本身的配置一样使用。在 Pydantic V2 中,情况不再如此。
- 在 Pydantic V2 中,要覆盖配置(就像您在
BaseModel
上使用model_config
一样),您可以使用@dataclass
装饰器上的config
参数。有关示例,请参阅 Dataclass 配置。
- 在 Pydantic V2 中,要覆盖配置(就像您在
config 的更改¶
-
在 Pydantic V2 中,要在模型上指定配置,您应该将名为
model_config
的类属性设置为一个 dict,其中包含您希望用作配置的键/值对。Pydantic V1 行为在父BaseModel
子类的命名空间中创建名为Config
的类现在已弃用。 -
当子类化模型时,
model_config
属性会被继承。如果您想为许多模型使用具有给定配置的基类,这将非常有用。请注意,如果您从多个BaseModel
子类继承,例如class MyModel(Model1, Model2)
,则来自两个模型的model_config
属性中的非默认设置将被合并,并且对于在两者中定义的任何设置,来自Model2
的设置将覆盖来自Model1
的设置。 -
以下配置设置已被删除
allow_mutation
— 此项已被删除。您应该能够等效地使用 frozen(当前用法的逆向)。error_msg_templates
fields
— 这是各种 bug 的来源,因此已被删除。您应该能够使用Annotated
在字段上根据需要修改它们。getter_dict
—orm_mode
已被删除,并且此实现细节不再必要。smart_union
- Pydantic V2 中的默认union_mode
是'smart'
。underscore_attrs_are_private
— Pydantic V2 行为现在与在 Pydantic V1 中始终设置为True
时相同。json_loads
json_dumps
copy_on_model_validation
post_init_call
-
以下配置设置已重命名
allow_population_by_field_name
→populate_by_name
(或从 v2.11 开始为validate_by_name
)anystr_lower
→str_to_lower
anystr_strip_whitespace
→str_strip_whitespace
anystr_upper
→str_to_upper
keep_untouched
→ignored_types
max_anystr_length
→str_max_length
min_anystr_length
→str_min_length
orm_mode
→from_attributes
schema_extra
→json_schema_extra
validate_all
→validate_default
有关更多详细信息,请参阅 ConfigDict
API 参考。
validators 的更改¶
@validator
和 @root_validator
已弃用¶
@validator
已被弃用,应替换为@field_validator
,后者提供了各种新功能和改进。- 新的
@field_validator
装饰器没有each_item
关键字参数;您要应用于泛型容器中项目的验证器应通过注解类型参数来添加。有关详细信息,请参阅 Annotated 元数据中的验证器。这看起来像list[Annotated[int, Field(ge=0)]]
- 即使您继续使用已弃用的
@validator
装饰器,您也不能再将field
或config
参数添加到验证器函数的签名中。如果您需要访问这些参数,则需要迁移到@field_validator
— 有关更多详细信息,请参阅下一节。 - 如果您对验证器函数使用
always=True
关键字参数,请注意,即使对于默认值,也会应用注解类型的标准验证器,而不仅仅是自定义验证器。例如,尽管下面的验证器永远不会出错,但以下代码会引发ValidationError
- 新的
注意
为避免这种情况,您可以在 Field
函数中使用 validate_default
参数。当设置为 True
时,它会模拟 Pydantic v1 中 always=True
的行为。但是,鼓励使用新的 validate_default
方法,因为它提供了更大的灵活性和控制。
from pydantic import BaseModel, validator
class Model(BaseModel):
x: str = 1
@validator('x', always=True)
@classmethod
def validate_x(cls, v):
return v
Model()
@root_validator
已被弃用,应替换为@model_validator
,后者也提供了新功能和改进。- 在某些情况下(例如当
model_config['validate_assignment'] is True
时的赋值),@model_validator
装饰器将接收模型的实例,而不是值字典。您可能需要小心处理这种情况。 - 即使您继续使用已弃用的
@root_validator
装饰器,由于验证逻辑中的重构,您也不能再使用skip_on_failure=False
运行(这是此关键字参数的默认值,因此必须显式设置为True
)。
- 在某些情况下(例如当
@validator
允许的签名更改¶
在 Pydantic V1 中,由 @validator
包装的函数可以接收带有关于正在验证的内容的元数据的关键字参数。Pydantic V2 中的 @field_validator
中删除了一些这些参数
config
:Pydantic V2 的配置现在是一个字典而不是一个类,这意味着此参数不再向后兼容。如果您需要访问配置,则应迁移到@field_validator
并使用info.config
。field
:此参数曾经是一个ModelField
对象,这是一个准内部类,在 Pydantic V2 中不再存在。仍然可以通过使用info.field_name
中的字段名称索引到cls.model_fields
来访问此信息的大部分内容
from pydantic import BaseModel, ValidationInfo, field_validator
class Model(BaseModel):
x: int
@field_validator('x')
def val_x(cls, v: int, info: ValidationInfo) -> int:
assert info.config is not None
print(info.config.get('title'))
#> Model
print(cls.model_fields[info.field_name].is_required())
#> True
return v
Model(x=1)
TypeError
不再在 validators 中转换为 ValidationError
¶
以前,当在验证器函数中引发 TypeError
时,该错误将被包装到 ValidationError
中,并且在某些情况下(例如使用 FastAPI),这些错误可能会显示给最终用户。这导致了各种不良行为 — 例如,使用错误的签名调用函数可能会产生面向用户的 ValidationError
。
但是,在 Pydantic V2 中,当在验证器中引发 TypeError
时,它不再转换为 ValidationError
import pytest
from pydantic import BaseModel, field_validator # or validator
class Model(BaseModel):
x: int
@field_validator('x')
def val_x(cls, v: int) -> int:
return str.lower(v) # raises a TypeError
with pytest.raises(TypeError):
Model(x=1)
这适用于所有验证装饰器。
Validator 行为更改¶
Pydantic V2 包括一些类型强制转换的更改。例如
- 将
int
、float
和Decimal
值强制转换为字符串现在是可选的,默认情况下禁用,请参阅 将数字强制转换为字符串。 - 成对的迭代器不再强制转换为 dict。
有关 Pydantic V2 类型强制转换默认值的详细信息,请参阅 转换表。
allow_reuse
关键字参数不再必要¶
以前,Pydantic 跟踪装饰器中的“重用”函数,因为这是错误的常见来源。我们通过比较函数的完全限定名称(模块名称 + 函数名称)来做到这一点,这可能会导致误报。当这是有意为之时,可以使用 allow_reuse
关键字参数禁用此功能。
我们检测重复定义函数的方法已得到彻底检查,仅针对单个类中的重新定义发出错误,从而减少误报,并使行为更符合类型检查器和 linter 对于在单个类定义中多次定义同名方法时给出的错误。
在几乎所有情况下,如果您使用 allow_reuse=True
,您应该能够简单地删除该关键字参数,并使事情按预期继续工作。
@validate_arguments
已重命名为 @validate_call
¶
在 Pydantic V2 中,@validate_arguments
装饰器已重命名为 @validate_call
。
在 Pydantic V1 中,装饰的函数添加了各种属性,例如 raw_function
和 validate
(可用于验证参数而无需实际调用装饰的函数)。由于这些属性的使用有限以及面向性能的实现更改,我们没有在 @validate_call
中保留此功能。
输入类型未被保留¶
在 Pydantic V1 中,我们付出了巨大的努力来保留泛型集合的所有字段输入的类型,当它们是字段注解的适当子类型时。例如,给定注解 Mapping[str, int]
,如果您传入 collection.Counter()
,您将获得 collection.Counter()
作为值。
在 V2 中支持此行为会对一般情况产生负面的性能影响(我们每次都必须检查类型),并且会增加验证的复杂性。此外,即使在 V1 中,此行为也是不一致且部分损坏的:它不适用于许多类型(str
、UUID
等),并且对于泛型集合,如果不进行大量特殊情况处理,则无法正确重建原始输入(考虑 ChainMap
;重建输入是必要的,因为我们需要在验证后替换值,例如,如果将字符串强制转换为 int)。
在 Pydantic V2 中,我们不再尝试在所有情况下都保留输入类型;相反,我们只保证输出类型将与类型注解匹配。
回到 Mapping
示例,我们保证输出将是有效的 Mapping
,并且在实践中它将是一个普通的 dict
from typing import Mapping
from pydantic import TypeAdapter
class MyDict(dict):
pass
ta = TypeAdapter(Mapping[str, int])
v = ta.validate_python(MyDict())
print(type(v))
#> <class 'dict'>
from collections.abc import Mapping
from pydantic import TypeAdapter
class MyDict(dict):
pass
ta = TypeAdapter(Mapping[str, int])
v = ta.validate_python(MyDict())
print(type(v))
#> <class 'dict'>
如果您希望输出类型是特定类型,请考虑将其注解为该类型或实现自定义验证器
from typing import Annotated, Any, Mapping, TypeVar
from pydantic import (
TypeAdapter,
ValidationInfo,
ValidatorFunctionWrapHandler,
WrapValidator,
)
def restore_input_type(
value: Any, handler: ValidatorFunctionWrapHandler, _info: ValidationInfo
) -> Any:
return type(value)(handler(value))
T = TypeVar('T')
PreserveType = Annotated[T, WrapValidator(restore_input_type)]
ta = TypeAdapter(PreserveType[Mapping[str, int]])
class MyDict(dict):
pass
v = ta.validate_python(MyDict())
assert type(v) is MyDict
from typing import Annotated, Any, TypeVar
from collections.abc import Mapping
from pydantic import (
TypeAdapter,
ValidationInfo,
ValidatorFunctionWrapHandler,
WrapValidator,
)
def restore_input_type(
value: Any, handler: ValidatorFunctionWrapHandler, _info: ValidationInfo
) -> Any:
return type(value)(handler(value))
T = TypeVar('T')
PreserveType = Annotated[T, WrapValidator(restore_input_type)]
ta = TypeAdapter(PreserveType[Mapping[str, int]])
class MyDict(dict):
pass
v = ta.validate_python(MyDict())
assert type(v) is MyDict
虽然我们不保证在所有地方都保留输入类型,但我们确实为 BaseModel
的子类和 dataclasses 保留它们
import pydantic.dataclasses
from pydantic import BaseModel
class InnerModel(BaseModel):
x: int
class OuterModel(BaseModel):
inner: InnerModel
class SubInnerModel(InnerModel):
y: int
m = OuterModel(inner=SubInnerModel(x=1, y=2))
print(m)
#> inner=SubInnerModel(x=1, y=2)
@pydantic.dataclasses.dataclass
class InnerDataclass:
x: int
@pydantic.dataclasses.dataclass
class SubInnerDataclass(InnerDataclass):
y: int
@pydantic.dataclasses.dataclass
class OuterDataclass:
inner: InnerDataclass
d = OuterDataclass(inner=SubInnerDataclass(x=1, y=2))
print(d)
#> OuterDataclass(inner=SubInnerDataclass(x=1, y=2))
标准类型处理的更改¶
Dicts¶
成对的迭代器(包括空迭代器)不再通过类型为 dict
的字段的验证。
Unions¶
虽然联合类型仍将尝试从左到右验证每个选项,但它们现在尽可能保留输入的类型,即使正确的类型不是输入将通过验证的第一个选项。作为演示,请考虑以下示例
from typing import Union
from pydantic import BaseModel
class Model(BaseModel):
x: Union[int, str]
print(Model(x='1'))
#> x='1'
from pydantic import BaseModel
class Model(BaseModel):
x: int | str
print(Model(x='1'))
#> x='1'
在 Pydantic V1 中,打印的结果将是 x=1
,因为该值将作为 int
通过验证。在 Pydantic V2 中,我们认识到该值是其中一种情况的实例,并短路了标准联合验证。
要恢复到 V1 的非短路从左到右行为,请使用 Field(union_mode='left_to_right')
注解联合。有关更多详细信息,请参阅 联合模式。
必需、可选和可空字段¶
Pydantic V2 更改了用于指定注解为 Optional
的字段是必需的(即,没有默认值)还是非必需的(即,具有 None
的默认值或相应类型的任何其他值)的一些逻辑,现在更紧密地匹配 dataclasses
的行为。同样,注解为 Any
的字段不再具有 None
的默认值。
下表描述了 V2 中字段注解的行为
状态 | 字段定义 |
---|---|
必需,不能为 None |
f1: str |
非必需,不能为 None ,默认为 'abc' |
f2: str = 'abc' |
必需,可以为 None |
f3: Optional[str] |
非必需,可以为 None ,默认为 None |
f4: Optional[str] = None |
非必需,可以为 None ,默认为 'abc' |
f5: Optional[str] = 'abc' |
必需,可以是任何类型(包括 None ) |
f6: Any |
非必需,可以是任何类型(包括 None ) |
f7: Any = None |
注意
被注解为 typing.Optional[T]
的字段将是必需的,并允许值为 None
。但这并不意味着该字段具有 None
的默认值。 (这是 V1 版本的一个重大变更。)
注意
任何提供的默认值都会使字段变为非必需。
这是一个代码示例,演示了上述内容
from typing import Optional
from pydantic import BaseModel, ValidationError
class Foo(BaseModel):
f1: str # required, cannot be None
f2: Optional[str] # required, can be None - same as str | None
f3: Optional[str] = None # not required, can be None
f4: str = 'Foobar' # not required, but cannot be None
try:
Foo(f1=None, f2=None, f4='b')
except ValidationError as e:
print(e)
"""
1 validation error for Foo
f1
Input should be a valid string [type=string_type, input_value=None, input_type=NoneType]
"""
from pydantic import BaseModel, ValidationError
class Foo(BaseModel):
f1: str # required, cannot be None
f2: str | None # required, can be None - same as str | None
f3: str | None = None # not required, can be None
f4: str = 'Foobar' # not required, but cannot be None
try:
Foo(f1=None, f2=None, f4='b')
except ValidationError as e:
print(e)
"""
1 validation error for Foo
f1
Input should be a valid string [type=string_type, input_value=None, input_type=NoneType]
"""
字符串的模式/正则表达式¶
Pydantic V1 使用 Python 的 regex 库。Pydantic V2 使用 Rust 的 regex crate。这个 crate 不仅仅是“Rust 版本的正则表达式”,它是一种完全不同的正则表达式方法。特别是,它承诺对字符串进行线性时间搜索,以换取放弃一些功能(即环视和反向引用)。我们认为这是一个值得的权衡,特别是因为 Pydantic 用于验证不受信任的输入,确保事情不会因不受信任的输入而意外地以指数时间运行非常重要。另一方面,对于任何不使用这些复杂正则表达式功能的人来说,正则表达式验证应该会快几个数量级,因为它是在 Rust 中完成的,并且是线性时间的。
如果您仍然想使用 Python 的 regex 库,您可以使用 regex_engine
配置设置。
从浮点数到整数的类型转换¶
在 V1 中,每当字段被注解为 int
时,任何浮点数值都会被接受,如果浮点数值包含非零小数部分,这可能会导致潜在的数据丢失。在 V2 中,仅当小数部分为零时,才允许从浮点数到整数的类型转换。
from pydantic import BaseModel, ValidationError
class Model(BaseModel):
x: int
print(Model(x=10.0))
#> x=10
try:
Model(x=10.2)
except ValidationError as err:
print(err)
"""
1 validation error for Model
x
Input should be a valid integer, got a number with a fractional part [type=int_from_float, input_value=10.2, input_type=float]
"""
TypeAdapter
的介绍¶
Pydantic V1 对验证或序列化非 BaseModel
类型类型的支持较弱。
要使用它们,您必须创建一个“根”模型,或者使用 pydantic.tools
中的实用程序函数(即 parse_obj_as
和 schema_of
)。
在 Pydantic V2 中,这容易得多:TypeAdapter
类允许您创建一个对象,该对象具有用于验证、序列化和为任意类型生成 JSON 模式的方法。这可以完全替代 parse_obj_as
和 schema_of
(现在已弃用),并且还涵盖了“根”模型的一些用例。(RootModel
,如上所述,涵盖了其他用例。)
from pydantic import TypeAdapter
adapter = TypeAdapter(list[int])
assert adapter.validate_python(['1', '2', '3']) == [1, 2, 3]
print(adapter.json_schema())
#> {'items': {'type': 'integer'}, 'type': 'array'}
由于常见类型检查器推断泛型类型的限制,为了在某些情况下获得正确的类型提示,您可能需要显式指定泛型参数。
from pydantic import TypeAdapter
adapter = TypeAdapter[str | int](str | int)
...
有关更多信息,请参阅 Type Adapter。
定义自定义类型¶
我们已经彻底修改了在 pydantic 中定义自定义类型的方式。
我们已经公开了用于生成 pydantic-core
和 JSON 模式的钩子,即使在使用您自己的自定义类型时,您也可以获得 Pydantic V2 的所有性能优势。
我们还引入了使用 typing.Annotated
为您自己的类型添加自定义验证的方法。
主要变化是
__get_validators__
应该替换为__get_pydantic_core_schema__
。有关更多信息,请参阅 自定义数据类型。__modify_schema__
变为__get_pydantic_json_schema__
。有关更多信息,请参阅 JSON 模式自定义。
此外,您可以使用 typing.Annotated
通过注解类型而不是修改类型本身来修改或提供类型的 __get_pydantic_core_schema__
和 __get_pydantic_json_schema__
函数。这为将第三方类型与 Pydantic 集成提供了一种强大而灵活的机制,并且在某些情况下可能有助于您删除 Pydantic V1 中引入的用于解决自定义类型限制的 hack。
有关更多信息,请参阅 自定义数据类型。
JSON 模式生成的变更¶
多年来,我们收到了许多关于更改 pydantic 生成的 JSON 模式的请求。
在 Pydantic V2 中,我们试图解决许多常见请求
Optional
字段的 JSON 模式现在指示允许值null
。Decimal
类型现在在 JSON 模式中公开(并序列化)为字符串。- JSON 模式不再将 namedtuples 保留为 namedtuples。
- 我们默认生成的 JSON 模式现在目标是 draft 2020-12(带有一些 OpenAPI 扩展)。
- 当它们不同时,您现在可以指定是否需要表示验证输入的 JSON 模式,还是来自序列化的输出的 JSON 模式。
但是,多年来,对于我们未选择实施的更改,已经有很多合理的请求。
在 Pydantic V1 中,即使您愿意自己实施更改,也非常困难,因为 JSON 模式生成过程涉及各种递归函数调用;要覆盖一个,您必须复制并修改整个实现。
在 Pydantic V2 中,我们的设计目标之一是使自定义 JSON 模式生成更容易。为此,我们引入了类 GenerateJsonSchema
,它实现了将类型的 pydantic-core 模式转换为 JSON 模式。通过设计,此类将 JSON 模式生成过程分解为更小的方法,这些方法可以在子类中轻松覆盖,以修改生成 JSON 模式的“全局”方法。
可用于生成 JSON 模式的各种方法(例如 BaseModel.model_json_schema
或 TypeAdapter.json_schema
)接受关键字参数 schema_generator: type[GenerateJsonSchema] = GenerateJsonSchema
,您可以将自定义子类传递给这些方法,以便使用您自己的方法来生成 JSON 模式。
希望这意味着,如果您不同意我们所做的任何选择,或者如果您依赖于 Pydantic V1 中已在 Pydantic V2 中更改的行为,则可以使用自定义 schema_generator
,根据您的应用程序的需要修改 GenerateJsonSchema
类。
BaseSettings
已移动到 pydantic-settings
¶
BaseSettings
,Pydantic 设置管理的基础对象,已移动到一个单独的包 pydantic-settings
。
此外,parse_env_var
类方法已被删除。因此,您需要自定义设置源以拥有自己的解析函数。
颜色和支付卡号已移动到 pydantic-extra-types
¶
以下特殊用途类型已移动到 Pydantic Extra Types 包,如果需要,可以单独安装。
pydantic.networks
中的 Url 和 Dsn 类型不再继承自 str
¶
在 Pydantic V1 中,AnyUrl
类型继承自 str
,所有其他 Url
和 Dsn
类型都继承自这些类型。在 Pydantic V2 中,这些类型是使用 Annotated
构建在两个新的 Url
和 MultiHostUrl
类之上的。
从 str
继承有优点和缺点,对于 V2,我们决定最好删除它。要在期望 str
的 API 中使用这些类型,您现在需要转换它们(使用 str(url)
)。
Pydantic V2 使用 Rust 的 Url crate 进行 URL 验证。一些 URL 验证与 V1 中之前的行为略有不同。一个值得注意的区别是,新的 Url
类型会在验证版本的末尾附加斜杠,如果未包含路径,即使在 Url
类型构造函数的参数中未指定斜杠。请参阅下面的示例以了解此行为
from pydantic import AnyUrl
assert str(AnyUrl(url='https://google.com')) == 'https://google.com/'
assert str(AnyUrl(url='https://google.com/')) == 'https://google.com/'
assert str(AnyUrl(url='https://google.com/api')) == 'https://google.com/api'
assert str(AnyUrl(url='https://google.com/api/')) == 'https://google.com/api/'
如果您仍然想使用没有附加斜杠的旧行为,请查看此解决方案。
约束类型¶
Constrained*
类已删除,您应该用 Annotated[<type>, Field(...)]
替换它们,例如
from pydantic import BaseModel, ConstrainedInt
class MyInt(ConstrainedInt):
ge = 0
class Model(BaseModel):
x: MyInt
...变为
from typing import Annotated
from pydantic import BaseModel, Field
MyInt = Annotated[int, Field(ge=0)]
class Model(BaseModel):
x: MyInt
在 通过 Annotated
组合类型 文档中阅读更多相关信息。
对于 ConstrainedStr
,您可以使用 StringConstraints
代替。
Mypy 插件¶
Pydantic V2 在 pydantic.mypy
中包含一个 mypy 插件。
当使用 V1 功能 时,可能还需要启用 pydantic.v1.mypy
插件。
要配置 mypy 插件
[mypy]
plugins = pydantic.mypy, pydantic.v1.mypy # include `.v1.mypy` if required.
[tool.mypy]
plugins = [
"pydantic.mypy",
"pydantic.v1.mypy", # include `.v1.mypy` if required.
]
其他变更¶
- 放弃了对
email-validator<2.0.0
的支持。请务必使用pip install -U email-validator
更新。
在 Pydantic V2 中移动¶
Pydantic V1 | Pydantic V2 |
---|---|
pydantic.BaseSettings |
pydantic_settings.BaseSettings |
pydantic.color |
pydantic_extra_types.color |
pydantic.types.PaymentCardBrand |
pydantic_extra_types.PaymentCardBrand |
pydantic.types.PaymentCardNumber |
pydantic_extra_types.PaymentCardNumber |
pydantic.utils.version_info |
pydantic.version.version_info |
pydantic.error_wrappers.ValidationError |
pydantic.ValidationError |
pydantic.utils.to_camel |
pydantic.alias_generators.to_pascal |
pydantic.utils.to_lower_camel |
pydantic.alias_generators.to_camel |
pydantic.PyObject |
pydantic.ImportString |
在 Pydantic V2 中已弃用和移动¶
Pydantic V1 | Pydantic V2 |
---|---|
pydantic.tools.schema_of |
pydantic.deprecated.tools.schema_of |
pydantic.tools.parse_obj_as |
pydantic.deprecated.tools.parse_obj_as |
pydantic.tools.schema_json_of |
pydantic.deprecated.tools.schema_json_of |
pydantic.json.pydantic_encoder |
pydantic.deprecated.json.pydantic_encoder |
pydantic.validate_arguments |
pydantic.deprecated.decorator.validate_arguments |
pydantic.json.custom_pydantic_encoder |
pydantic.deprecated.json.custom_pydantic_encoder |
pydantic.json.ENCODERS_BY_TYPE |
pydantic.deprecated.json.ENCODERS_BY_TYPE |
pydantic.json.timedelta_isoformat |
pydantic.deprecated.json.timedelta_isoformat |
pydantic.decorator.validate_arguments |
pydantic.deprecated.decorator.validate_arguments |
pydantic.class_validators.validator |
pydantic.deprecated.class_validators.validator |
pydantic.class_validators.root_validator |
pydantic.deprecated.class_validators.root_validator |
pydantic.utils.deep_update |
pydantic.v1.utils.deep_update |
pydantic.utils.GetterDict |
pydantic.v1.utils.GetterDict |
pydantic.utils.lenient_issubclass |
pydantic.v1.utils.lenient_issubclass |
pydantic.utils.lenient_isinstance |
pydantic.v1.utils.lenient_isinstance |
pydantic.utils.is_valid_field |
pydantic.v1.utils.is_valid_field |
pydantic.utils.update_not_none |
pydantic.v1.utils.update_not_none |
pydantic.utils.import_string |
pydantic.v1.utils.import_string |
pydantic.utils.Representation |
pydantic.v1.utils.Representation |
pydantic.utils.ROOT_KEY |
pydantic.v1.utils.ROOT_KEY |
pydantic.utils.smart_deepcopy |
pydantic.v1.utils.smart_deepcopy |
pydantic.utils.sequence_like |
pydantic.v1.utils.sequence_like |
在 Pydantic V2 中已删除¶
pydantic.ConstrainedBytes
pydantic.ConstrainedDate
pydantic.ConstrainedDecimal
pydantic.ConstrainedFloat
pydantic.ConstrainedFrozenSet
pydantic.ConstrainedInt
pydantic.ConstrainedList
pydantic.ConstrainedSet
pydantic.ConstrainedStr
pydantic.JsonWrapper
pydantic.NoneBytes
- 这是
None | bytes
的别名。
- 这是
pydantic.NoneStr
- 这是
None | str
的别名。
- 这是
pydantic.NoneStrBytes
- 这是
None | str | bytes
的别名。
- 这是
pydantic.Protocol
pydantic.Required
pydantic.StrBytes
- 这是
str | bytes
的别名。
- 这是
pydantic.compiled
pydantic.config.get_config
pydantic.config.inherit_config
pydantic.config.prepare_config
pydantic.create_model_from_namedtuple
pydantic.create_model_from_typeddict
pydantic.dataclasses.create_pydantic_model_from_dataclass
pydantic.dataclasses.make_dataclass_validator
pydantic.dataclasses.set_validation
pydantic.datetime_parse.parse_date
pydantic.datetime_parse.parse_time
pydantic.datetime_parse.parse_datetime
pydantic.datetime_parse.parse_duration
pydantic.error_wrappers.ErrorWrapper
pydantic.errors.AnyStrMaxLengthError
pydantic.errors.AnyStrMinLengthError
pydantic.errors.ArbitraryTypeError
pydantic.errors.BoolError
pydantic.errors.BytesError
pydantic.errors.CallableError
pydantic.errors.ClassError
pydantic.errors.ColorError
pydantic.errors.ConfigError
pydantic.errors.DataclassTypeError
pydantic.errors.DateError
pydantic.errors.DateNotInTheFutureError
pydantic.errors.DateNotInThePastError
pydantic.errors.DateTimeError
pydantic.errors.DecimalError
pydantic.errors.DecimalIsNotFiniteError
pydantic.errors.DecimalMaxDigitsError
pydantic.errors.DecimalMaxPlacesError
pydantic.errors.DecimalWholeDigitsError
pydantic.errors.DictError
pydantic.errors.DurationError
pydantic.errors.EmailError
pydantic.errors.EnumError
pydantic.errors.EnumMemberError
pydantic.errors.ExtraError
pydantic.errors.FloatError
pydantic.errors.FrozenSetError
pydantic.errors.FrozenSetMaxLengthError
pydantic.errors.FrozenSetMinLengthError
pydantic.errors.HashableError
pydantic.errors.IPv4AddressError
pydantic.errors.IPv4InterfaceError
pydantic.errors.IPv4NetworkError
pydantic.errors.IPv6AddressError
pydantic.errors.IPv6InterfaceError
pydantic.errors.IPv6NetworkError
pydantic.errors.IPvAnyAddressError
pydantic.errors.IPvAnyInterfaceError
pydantic.errors.IPvAnyNetworkError
pydantic.errors.IntEnumError
pydantic.errors.IntegerError
pydantic.errors.InvalidByteSize
pydantic.errors.InvalidByteSizeUnit
pydantic.errors.InvalidDiscriminator
pydantic.errors.InvalidLengthForBrand
pydantic.errors.JsonError
pydantic.errors.JsonTypeError
pydantic.errors.ListError
pydantic.errors.ListMaxLengthError
pydantic.errors.ListMinLengthError
pydantic.errors.ListUniqueItemsError
pydantic.errors.LuhnValidationError
pydantic.errors.MissingDiscriminator
pydantic.errors.MissingError
pydantic.errors.NoneIsAllowedError
pydantic.errors.NoneIsNotAllowedError
pydantic.errors.NotDigitError
pydantic.errors.NotNoneError
pydantic.errors.NumberNotGeError
pydantic.errors.NumberNotGtError
pydantic.errors.NumberNotLeError
pydantic.errors.NumberNotLtError
pydantic.errors.NumberNotMultipleError
pydantic.errors.PathError
pydantic.errors.PathNotADirectoryError
pydantic.errors.PathNotAFileError
pydantic.errors.PathNotExistsError
pydantic.errors.PatternError
pydantic.errors.PyObjectError
pydantic.errors.PydanticTypeError
pydantic.errors.PydanticValueError
pydantic.errors.SequenceError
pydantic.errors.SetError
pydantic.errors.SetMaxLengthError
pydantic.errors.SetMinLengthError
pydantic.errors.StrError
pydantic.errors.StrRegexError
pydantic.errors.StrictBoolError
pydantic.errors.SubclassError
pydantic.errors.TimeError
pydantic.errors.TupleError
pydantic.errors.TupleLengthError
pydantic.errors.UUIDError
pydantic.errors.UUIDVersionError
pydantic.errors.UrlError
pydantic.errors.UrlExtraError
pydantic.errors.UrlHostError
pydantic.errors.UrlHostTldError
pydantic.errors.UrlPortError
pydantic.errors.UrlSchemeError
pydantic.errors.UrlSchemePermittedError
pydantic.errors.UrlUserInfoError
pydantic.errors.WrongConstantError
pydantic.main.validate_model
pydantic.networks.stricturl
pydantic.parse_file_as
pydantic.parse_raw_as
pydantic.stricturl
pydantic.tools.parse_file_as
pydantic.tools.parse_raw_as
pydantic.types.JsonWrapper
pydantic.types.NoneBytes
pydantic.types.NoneStr
pydantic.types.NoneStrBytes
pydantic.types.PyObject
pydantic.types.StrBytes
pydantic.typing.evaluate_forwardref
pydantic.typing.AbstractSetIntStr
pydantic.typing.AnyCallable
pydantic.typing.AnyClassMethod
pydantic.typing.CallableGenerator
pydantic.typing.DictAny
pydantic.typing.DictIntStrAny
pydantic.typing.DictStrAny
pydantic.typing.IntStr
pydantic.typing.ListStr
pydantic.typing.MappingIntStrAny
pydantic.typing.NoArgAnyCallable
pydantic.typing.NoneType
pydantic.typing.ReprArgs
pydantic.typing.SetStr
pydantic.typing.StrPath
pydantic.typing.TupleGenerator
pydantic.typing.WithArgsTypes
pydantic.typing.all_literal_values
pydantic.typing.display_as_type
pydantic.typing.get_all_type_hints
pydantic.typing.get_args
pydantic.typing.get_origin
pydantic.typing.get_sub_types
pydantic.typing.is_callable_type
pydantic.typing.is_classvar
pydantic.typing.is_finalvar
pydantic.typing.is_literal_type
pydantic.typing.is_namedtuple
pydantic.typing.is_new_type
pydantic.typing.is_none_type
pydantic.typing.is_typeddict
pydantic.typing.is_typeddict_special
pydantic.typing.is_union
pydantic.typing.new_type_supertype
pydantic.typing.resolve_annotations
pydantic.typing.typing_base
pydantic.typing.update_field_forward_refs
pydantic.typing.update_model_forward_refs
pydantic.utils.ClassAttribute
pydantic.utils.DUNDER_ATTRIBUTES
pydantic.utils.PyObjectStr
pydantic.utils.ValueItems
pydantic.utils.almost_equal_floats
pydantic.utils.get_discriminator_alias_and_values
pydantic.utils.get_model
pydantic.utils.get_unique_discriminator_alias
pydantic.utils.in_ipython
pydantic.utils.is_valid_identifier
pydantic.utils.path_type
pydantic.utils.validate_field_name
pydantic.validate_model