迁移指南
Pydantic V2 引入了许多 API 变更,包括一些破坏性变更。
本指南重点介绍了最重要的变更,以帮助您将代码从 Pydantic V1 迁移到 Pydantic V2。
安装 Pydantic V2¶
Pydantic V2 现在是 Pydantic 的当前生产版本。 您可以从 PyPI 安装 Pydantic V2:
pip install -U pydantic
如果您遇到任何问题,请在 GitHub 上创建问题并使用 bug V2 标签。
这将帮助我们积极监控和跟踪错误,并持续改进库的性能。
如果因任何原因需要继续使用最新的 Pydantic V1,请参阅下面的继续使用 Pydantic V1 功能部分,了解从 pydantic.v1 安装和导入的详细信息。
代码转换工具¶
我们创建了一个工具来帮助您迁移代码。该工具仍处于测试阶段,但我们希望它能帮助您更快地迁移代码。
您可以从 PyPI 安装该工具:
pip install bump-pydantic
用法很简单。如果您的项目结构是:
* repo_folder
* my_package
* <python 源文件> ...
那么您需要执行:
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 API 的访问,通过 pydantic.v1 导入。
例如,您可以使用 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.dev/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
Note
当使用 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'时相关) - 私有属性值;具有不同私有属性值的模型不再相等。
- 模型不再等于包含其数据的字典。
- 不同类型的非泛型模型永远不相等。
- 具有不同原始类型的泛型模型永远不相等。我们不要求精确的类型相等,因此,
例如,
MyGenericModel[Any]的实例可以与MyGenericModel[int]的实例相等。
- 模型只能与其他
- 我们已替换使用
__root__字段指定“自定义根模型”的方式,改用名为RootModel的新类型,旨在替换 Pydantic V1 中使用名为__root__字段的功能。注意,RootModel类型不再支持arbitrary_types_allowed配置设置。有关解释,请参阅此问题评论。 - 我们显著扩展了 Pydantic 自定义序列化的能力。特别是,我们
添加了
@field_serializer、@model_serializer和@computed_field装饰器,每个都解决了 Pydantic V1 中的 各种缺点。- 有关这些新装饰器的用法文档,请参阅自定义序列化器。
- 由于性能开销和实现复杂性,我们现在已弃用在模型配置中指定
json_encoders的支持。此功能最初是为了实现自定义 序列化逻辑而添加的,我们认为新的序列化装饰器在大多数常见场景中是更好的选择。
- 我们更改了与序列化模型子类相关的行为,当它们作为嵌套字段出现在父模型时。 在 V1 中,我们总是包含子类实例的所有字段。在 V2 中,当我们转储模型时,我们只 包含字段的注解类型上定义的字段。这有助于防止一些意外的安全错误。 您可以在模型导出文档的相关部分 阅读更多关于此的信息(包括如何选择退出此行为)。
GetterDict已被移除,因为它只是orm_mode的一个实现细节,而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"]}
# 普通的 json.dumps
print(json.dumps(v2_model.model_dump()))
#> {"a": ["fancy", "sushi"]}
# 修改后的 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 模式中。相反,任何您想要添加到 JSON 模式的
额外数据都应作为字典传递给 json_schema_extra 关键字参数。
在 Pydantic V1 中,alias 属性在没有设置别名时返回字段的名称。
在 Pydantic V2 中,此行为已更改为在没有设置别名时返回 None。
以下属性已从 Field 中移除或更改:
constmin_items(改用min_length)max_items(改用max_length)unique_itemsallow_mutation(改用frozen)regex(改用pattern)final(改用 typing.Final 类型提示)
字段约束不再自动推送到泛型的参数中。例如,您不能再通过提供 my_list: list[str] = Field(pattern=".*") 来验证列表的每个元素都匹配正则表达式。
相反,使用 typing.Annotated 在 str 本身上提供注解:my_list: list[Annotated[str, Field(pattern=".*")]]
数据类的变更¶
Pydantic 数据类继续用于在标准数据类上启用数据验证,而无需子类化 BaseModel。
Pydantic V2 对此数据类行为引入了以下变更:
- 当用作字段时,数据类(Pydantic 或普通)不再接受元组作为验证输入;应改用字典。
- Pydantic 数据类中的
__post_init__现在将在验证之后调用,而不是之前。- 因此,
__post_init_post_parse__方法变得冗余,已被移除。
- 因此,
- Pydantic 不再支持 Pydantic 数据类的
extra='allow',其中传递给初始化器的额外字段将 作为额外属性存储在数据类上。extra='ignore'仍然支持用于在解析数据时忽略 意外字段的目的,它们只是不会存储在实例上。 - Pydantic 数据类不再具有属性
__pydantic_model__,并且不再使用底层的BaseModel来执行验证或提供其他功能。- 要执行验证、生成 JSON 模式或利用
任何其他可能需要在 V1 中使用
__pydantic_model__的功能,您现在应该使用TypeAdapter包装数据类(下面更多讨论)并 使用其方法。
- 要执行验证、生成 JSON 模式或利用
任何其他可能需要在 V1 中使用
- 在 Pydantic V1 中,如果您使用普通(即非 Pydantic)数据类作为字段,父类型的配置将
被用作数据类本身的配置。在 Pydantic V2 中,情况不再如此。
- 在 Pydantic V2 中,要覆盖配置(就像在
BaseModel上使用model_config一样), 您可以在@dataclass装饰器上使用config参数。 有关示例,请参阅数据类配置。
- 在 Pydantic V2 中,要覆盖配置(就像在
配置的变更¶
-
在 Pydantic V2 中,要在模型上指定配置,您应设置一个名为
model_config的类属性,该属性是一个字典, 包含您想要用作配置的键/值对。在父BaseModel子类的命名空间中创建名为Config的类的 Pydantic V1 行为现在已被弃用。 -
当子类化模型时,
model_config属性会被继承。这在您希望为许多模型使用具有给定配置的基类时很有用。 注意,如果您从多个BaseModel子类继承,例如class MyModel(Model1, Model2), 来自两个模型的model_config属性中的非默认设置将被合并,对于在两个模型中定义的任何设置, 来自Model2的设置将覆盖来自Model1的设置。 -
以下配置设置已被移除:
allow_mutation— 已被移除。您应该能够等效地使用 frozen(与当前使用相反)。error_msg_templatesfields— 这是各种错误的来源,因此已被移除。 您应该能够在字段上使用Annotated来根据需要修改它们。getter_dict—orm_mode已被移除,此实现细节不再必要。smart_union- Pydantic V2 中的默认union_mode是'smart'。underscore_attrs_are_private— Pydantic V2 的行为现在与在 Pydantic V1 中始终设置 此值为True时相同。json_loadsjson_dumpscopy_on_model_validationpost_init_call
-
以下配置设置已重命名:
allow_population_by_field_name→populate_by_name(或从 v2.11 开始使用validate_by_name)anystr_lower→str_to_loweranystr_strip_whitespace→str_strip_whitespaceanystr_upper→str_to_upperkeep_untouched→ignored_typesmax_anystr_length→str_max_lengthmin_anystr_length→str_min_lengthorm_mode→from_attributesschema_extra→json_schema_extravalidate_all→validate_default
有关更多详细信息,请参阅 ConfigDict API 参考。
验证器的变更¶
@validator 和 @root_validator 已被弃用¶
@validator已被弃用,应替换为@field_validator,它提供了各种新功能 和改进。- 新的
@field_validator装饰器没有each_item关键字参数;您想要 应用于泛型容器内项目的验证器应通过注解类型参数来添加。有关 详细信息,请参阅注解元数据中的验证器。 这看起来像list[Annotated[int, Field(ge=0)]] - 即使您继续使用已弃用的
@validator装饰器,您也不能再添加field或config参数到验证器函数的签名中。如果您需要访问这些,您需要 迁移到@field_validator— 有关更多详细信息,请参阅下一节。 - 如果您在验证器函数中使用
always=True关键字参数,请注意,带注解类型的 标准验证器也将应用于默认值,而不仅仅是自定义验证器。例如, 尽管下面的验证器永远不会出错,但以下代码会引发ValidationError:
- 新的
Note
为避免这种情况,您可以在 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 转换为 ValidationError¶
以前,在验证器函数中引发 TypeError 时,该错误将被包装到 ValidationError 中,
并且在某些情况下(例如使用 FastAPI),这些错误可能会显示给最终用户。这导致了各种
不良行为 — 例如,使用错误签名调用函数可能会产生面向用户的 ValidationError。
然而,在 Pydantic V2 中,当在验证器中引发 TypeError 时,它不再转换为 ValidationError:
import pytest
from pydantic import BaseModel, field_validator
class Model(BaseModel):
x: int
@field_validator('x')
def val_x(cls, v: int) -> int:
return str.lower(v) # 引发 TypeError
with pytest.raises(TypeError):
Model(x=1)
这适用于所有验证装饰器。
验证器行为变更¶
Pydantic V2 包括一些类型强制转换的变更。例如:
- 将
int、float和Decimal值强制转换为字符串现在是可选的,默认禁用,请参阅 将数字强制转换为字符串。 - 不再将键值对的可迭代对象强制转换为字典。
有关 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;重建输入是必要的,因为我们需要在验证后替换值,例如
如果强制转换字符串为整数)。
在 Pydantic V2 中,我们不再尝试在所有情况下保留输入类型;相反,我们只承诺输出 类型将匹配类型注解。
回到 Mapping 示例,我们承诺输出将是有效的 Mapping,实际上它将是一个
普通的 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 collections.abc import Mapping
from typing import Annotated, Any, 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
虽然我们不承诺在所有地方保留输入类型,但我们确实为 BaseModel 的子类保留它们,
以及数据类:
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))
标准类型处理的变更¶
字典¶
键值对的可迭代对象(包括空可迭代对象)不再通过 dict 类型字段的验证。
联合类型¶
虽然联合类型仍然会尝试从左到右验证每个选择,但它们现在会在可能时保留输入的 类型,即使正确的类型不是输入将通过验证的第一个选择。 作为演示,请考虑以下示例:
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 |
Note
注解为 typing.Optional[T] 的字段将是必需的,并且将允许 None 值。
这并不意味着该字段具有 None 的默认值。(这是与 V1 的破坏性变更。)
Note
任何提供的默认值都会使字段成为非必需的。
以下是演示上述内容的代码示例:
from typing import Optional
from pydantic import BaseModel, ValidationError
class Foo(BaseModel):
f1: str # 必需,不能为 None
f2: Optional[str] # 必需,可以为 None - 与 str | None 相同
f3: Optional[str] = None # 非必需,可以为 None
f4: str = 'Foobar' # 非必需,但不能为 None
try:
Foo(f1=None, f2=None, f4='b')
except ValidationError as e:
print(e)
"""
1 validation error for Foo
f1
输入应为有效字符串 [type=string_type, input_value=None, input_type=NoneType]
"""
from pydantic import BaseModel, ValidationError
class Foo(BaseModel):
f1: str # 必需,不能为 None
f2: str | None # 必需,可以为 None - 与 str | None 相同
f3: str | None = None # 非必需,可以为 None
f4: str = 'Foobar' # 非必需,但不能为 None
try:
Foo(f1=None, f2=None, f4='b')
except ValidationError as e:
print(e)
"""
1 validation error for Foo
f1
输入应为有效字符串 [type=string_type, input_value=None, input_type=NoneType]
"""
模式/字符串的正则表达式¶
Pydantic V1 使用 Python 的正则表达式库。Pydantic V2 使用 Rust regex crate。 这个 crate 不仅仅是“正则表达式的 Rust 版本”,它是一种完全不同的正则表达式方法。 特别是,它承诺线性时间字符串搜索,以放弃几个特性(即环视和反向引用)为代价。 我们认为这是一个值得做出的权衡,特别是因为 Pydantic 用于验证不受信任的输入,确保事物不会根据不受信任的输入意外以指数时间运行非常重要。 另一方面,对于不使用这些特性的任何人来说,复杂的正则表达式验证应该快几个数量级,因为它在 Rust 中完成并且是线性时间。
如果您仍想使用 Python 的正则表达式库,可以使用 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
输入应为有效整数,得到一个带有小数部分的数字 [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)
...
有关更多信息,请参阅类型适配器。
定义自定义类型¶
我们彻底改革了在 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 中为解决自定义类型的限制而引入的黑客行为。
有关更多信息,请参阅自定义数据类型。
JSON 模式生成的变更¶
多年来,我们收到了许多更改 pydantic 生成的 JSON 模式的请求。
在 Pydantic V2 中,我们尝试解决许多常见请求:
Optional字段的 JSON 模式现在指示允许值null。Decimal类型现在在 JSON 模式(和序列化)中公开为字符串。- JSON 模式不再保留命名元组作为命名元组。
- 我们默认生成的 JSON 模式现在以草案 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 额外类型包, 如果需要,可以单独安装。
pydantic.networks 中的 Url 和 Dsn 类型不再继承自 str¶
在 Pydantic V1 中,AnyUrl 类型继承自 str,并且所有其他
Url 和 Dsn 类型都继承自这些。在 Pydantic V2 中,这些类型建立在两个新的 Url 和 MultiHostUrl
类之上,使用 Annotated。
继承自 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 包含一个 mypy 插件,位于
pydantic.mypy。
当使用 V1 功能时,
可能还需要启用 pydantic.v1.mypy 插件。
要配置 mypy 插件:
[mypy]
plugins = pydantic.mypy, pydantic.v1.mypy # 如果需要,包括 `.v1.mypy`。
[tool.mypy]
plugins = [
"pydantic.mypy",
"pydantic.v1.mypy", # 如果需要,包括 `.v1.mypy`。
]
其他变更¶
- 放弃了对
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_camel |
pydantic.alias_generators.to_camel |
pydantic.PyObject |
pydantic.types.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.ConstrainedBytespydantic.ConstrainedDatepydantic.ConstrainedDecimalpydantic.ConstrainedFloatpydantic.ConstrainedFrozenSetpydantic.ConstrainedIntpydantic.ConstrainedListpydantic.ConstrainedSetpydantic.ConstrainedStrpydantic.JsonWrapperpydantic.NoneBytes- 这是
None | bytes的别名。
- 这是
pydantic.NoneStr- 这是
None | str的别名。
- 这是
pydantic.NoneStrBytes- 这是
None | str | bytes的别名。
- 这是
pydantic.Protocolpydantic.Requiredpydantic.StrBytes- 这是
str | bytes的别名。
- 这是
pydantic.compiledpydantic.config.get_configpydantic.config.inherit_configpydantic.config.prepare_configpydantic.create_model_from_namedtuplepydantic.create_model_from_typeddictpydantic.dataclasses.create_pydantic_model_from_dataclasspydantic.dataclasses.make_dataclass_validatorpydantic.dataclasses.set_validationpydantic.datetime_parse.parse_datepydantic.datetime_parse.parse_timepydantic.datetime_parse.parse_datetimepydantic.datetime_parse.parse_durationpydantic.error_wrappers.ErrorWrapperpydantic.errors.AnyStrMaxLengthErrorpydantic.errors.AnyStrMinLengthErrorpydantic.errors.ArbitraryTypeErrorpydantic.errors.BoolErrorpydantic.errors.BytesErrorpydantic.errors.CallableErrorpydantic.errors.ClassErrorpydantic.errors.ColorErrorpydantic.errors.ConfigErrorpydantic.errors.DataclassTypeErrorpydantic.errors.DateErrorpydantic.errors.DateNotInTheFutureErrorpydantic.errors.DateNotInThePastErrorpydantic.errors.DateTimeErrorpydantic.errors.DecimalErrorpydantic.errors.DecimalIsNotFiniteErrorpydantic.errors.DecimalMaxDigitsErrorpydantic.errors.DecimalMaxPlacesErrorpydantic.errors.DecimalWholeDigitsErrorpydantic.errors.DictErrorpydantic.errors.DurationErrorpydantic.errors.EmailErrorpydantic.errors.EnumErrorpydantic.errors.EnumMemberErrorpydantic.errors.ExtraErrorpydantic.errors.FloatErrorpydantic.errors.FrozenSetErrorpydantic.errors.FrozenSetMaxLengthErrorpydantic.errors.FrozenSetMinLengthErrorpydantic.errors.HashableErrorpydantic.errors.IPv4AddressErrorpydantic.errors.IPv4InterfaceErrorpydantic.errors.IPv4NetworkErrorpydantic.errors.IPv6AddressErrorpydantic.errors.IPv6InterfaceErrorpydantic.errors.IPv6NetworkErrorpydantic.errors.IPvAnyAddressErrorpydantic.errors.IPvAnyInterfaceErrorpydantic.errors.IPvAnyNetworkErrorpydantic.errors.IntEnumErrorpydantic.errors.IntegerErrorpydantic.errors.InvalidByteSizepydantic.errors.InvalidByteSizeUnitpydantic.errors.InvalidDiscriminatorpydantic.errors.InvalidLengthForBrandpydantic.errors.JsonErrorpydantic.errors.JsonTypeErrorpydantic.errors.ListErrorpydantic.errors.ListMaxLengthErrorpydantic.errors.ListMinLengthErrorpydantic.errors.ListUniqueItemsErrorpydantic.errors.LuhnValidationErrorpydantic.errors.MissingDiscriminatorpydantic.errors.MissingErrorpydantic.errors.NoneIsAllowedErrorpydantic.errors.NoneIsNotAllowedErrorpydantic.errors.NotDigitErrorpydantic.errors.NotNoneErrorpydantic.errors.NumberNotGeErrorpydantic.errors.NumberNotGtErrorpydantic.errors.NumberNotLeErrorpydantic.errors.NumberNotLtErrorpydantic.errors.NumberNotMultipleErrorpydantic.errors.PathErrorpydantic.errors.PathNotADirectoryErrorpydantic.errors.PathNotAFileErrorpydantic.errors.PathNotExistsErrorpydantic.errors.PatternErrorpydantic.errors.PyObjectErrorpydantic.errors.PydanticTypeErrorpydantic.errors.PydanticValueErrorpydantic.errors.SequenceErrorpydantic.errors.SetErrorpydantic.errors.SetMaxLengthErrorpydantic.errors.SetMinLengthErrorpydantic.errors.StrErrorpydantic.errors.StrRegexErrorpydantic.errors.StrictBoolErrorpydantic.errors.SubclassErrorpydantic.errors.TimeErrorpydantic.errors.TupleErrorpydantic.errors.TupleLengthErrorpydantic.errors.UUIDErrorpydantic.errors.UUIDVersionErrorpydantic.errors.UrlErrorpydantic.errors.UrlExtraErrorpydantic.errors.UrlHostErrorpydantic.errors.UrlHostTldErrorpydantic.errors.UrlPortErrorpydantic.errors.UrlSchemeErrorpydantic.errors.UrlSchemePermittedErrorpydantic.errors.UrlUserInfoErrorpydantic.errors.WrongConstantErrorpydantic.main.validate_modelpydantic.networks.stricturlpydantic.parse_file_aspydantic.parse_raw_aspydantic.stricturlpydantic.tools.parse_file_aspydantic.tools.parse_raw_aspydantic.types.JsonWrapperpydantic.types.NoneBytespydantic.types.NoneStrpydantic.types.NoneStrBytespydantic.types.PyObjectpydantic.types.StrBytespydantic.typing.evaluate_forwardrefpydantic.typing.AbstractSetIntStrpydantic.typing.AnyCallablepydantic.typing.AnyClassMethodpydantic.typing.CallableGeneratorpydantic.typing.DictAnypydantic.typing.DictIntStrAnypydantic.typing.DictStrAnypydantic.typing.IntStrpydantic.typing.ListStrpydantic.typing.MappingIntStrAnypydantic.typing.NoArgAnyCallablepydantic.typing.NoneTypepydantic.typing.ReprArgspydantic.typing.SetStrpydantic.typing.StrPathpydantic.typing.TupleGeneratorpydantic.typing.WithArgsTypespydantic.typing.all_literal_valuespydantic.typing.display_as_typepydantic.typing.get_all_type_hintspydantic.typing.get_argspydantic.typing.get_originpydantic.typing.get_sub_typespydantic.typing.is_callable_typepydantic.typing.is_classvarpydantic.typing.is_finalvarpydantic.typing.is_literal_typepydantic.typing.is_namedtuplepydantic.typing.is_new_typepydantic.typing.is_none_typepydantic.typing.is_typeddictpydantic.typing.is_typeddict_specialpydantic.typing.is_unionpydantic.typing.new_type_supertypepydantic.typing.resolve_annotationspydantic.typing.typing_basepydantic.typing.update_field_forward_refspydantic.typing.update_model_forward_refspydantic.utils.ClassAttributepydantic.utils.DUNDER_ATTRIBUTESpydantic.utils.PyObjectStrpydantic.utils.ValueItemspydantic.utils.almost_equal_floatspydantic.utils.get_discriminator_alias_and_valuespydantic.utils.get_modelpydantic.utils.get_unique_discriminator_aliaspydantic.utils.in_ipythonpydantic.utils.is_valid_identifierpydantic.utils.path_typepydantic.utils.validate_field_namepydantic.validate_model