我正试图将我的代码更新到pydantic v2,但却找不到一个好方法来复制我在版本1中的自定义类型。我将使用自定义日期类型作为示例。最初的实现和用法看起来像这样:
from datetime import date
from pydantic import BaseModel
class CustomDate(date):
# Override POTENTIAL_FORMATS and fill it with date format strings to match your data
POTENTIAL_FORMATS = []
@classmethod
def __get_validators__(cls):
yield cls.validate_date
@classmethod
def validate_date(cls, field_value, values, field, config) -> date:
if type(field_value) is date:
return field_value
return to_date(field.name, field_value, cls.POTENTIAL_FORMATS, return_str=False)
class ExampleModel(BaseModel):
class MyDate(CustomDate):
POTENTIAL_FORMATS = ['%Y-%m-%d', '%Y/%m/%d']
dt: MyDate
我试着按照官方文档和下面的例子here来做,它基本上是工作的,但是info
参数没有我需要的字段(data
和field_name
)。尝试访问它们会导致AttributeError。
info.field_name
*** AttributeError: No attribute named 'field_name'
Annotated
和__get_pydantic_core_schema__
方法都存在此问题
from datetime import date
from typing import Annotated
from pydantic import BaseModel, BeforeValidator
from pydantic_core import core_schema
class CustomDate:
POTENTIAL_FORMATS = []
@classmethod
def validate(cls, field_value, info):
if type(field_value) is date:
return field_value
return to_date(info.field_name, field_value, potential_formats, return_str=False)
@classmethod
def __get_pydantic_core_schema__(cls, source, handler) -> core_schema.CoreSchema:
return core_schema.general_plain_validator_function(cls.validate)
def custom_date(potential_formats):
"""
:param potential_formats: A list of datetime format strings
"""
def validate_date(field_value, info) -> date:
if type(field_value) is date:
return field_value
return to_date(info.field_name, field_value, potential_formats, return_str=False)
CustomDate = Annotated[date, BeforeValidator(validate_date)]
return CustomDate
class ExampleModel(BaseModel):
class MyDate(CustomDate):
POTENTIAL_FORMATS = ['%Y-%m-%d', '%Y/%m/%d']
dt: MyDate
dt2: custom_date(['%Y-%m-%d', '%Y/%m/%d'])
如果我只是将validate_date
函数作为一个常规的field_validator
函数包含进来,我会得到info
,其中包含了我需要的所有字段,只有在与自定义类型一起使用时,我才会看到这个问题。如何编写一个自定义类型,使其可以访问以前验证过的字段和正在验证的字段的名称?
2条答案
按热度按时间4szc88ey1#
一种更简单的方法是通过Annotated类型执行验证。但是,在某些情况下,您可能需要完全自定义的类型。
注解字段
(The简单的方法)
下面是一个测试,它确保了预期的行为:
全定制类型
您遇到的问题与pydantic执行验证的顺序有关。假设
date
有自己的核心架构(例如:将验证时间戳或类似的转换),您将希望在核心验证之前执行验证。相关答案(使用更简单的代码):在Pydantic v2中定义自定义类型
要解决这个问题,您需要在自定义类型中定义
__get_pydantic_core_schema__
。我在下面链接了模式验证,它允许将多个类型合并为一个(例如:假设你想把datetime
转换成日期,你可以在链中这样做)。我还使用了general_plain_validator_function
,它不需要特定的模式来操作(最普通的选择)。Pydantic V2中创建自定义Date类型的代码:
您可以使用已定义的类型,也可以通过子类化该类型来自定义
allow_formats
:这里有一个快速测试,显示事情正在工作:
l7wslrjt2#
从2.4版开始,你可以将field_name和data放在一起。点击这里查看更新的文档。
现在我的自定义数据类型的第一个版本看起来像这样:
我需要改变的只是我使用的core_schema验证器函数。第二个版本的自定义数据类型(使用Annotated的)现在可以正常工作,没有任何更改。
Pydantic 2.4之前
根据this feature request,在自定义类型验证器中访问
info.data
和info.field_name
目前在v2中是不可能的。如果你只需要
info.data
,那么看起来你可以用core_schema.field_before_validator_function
定义你的验证器(我猜所有的field_*
验证器都可以工作),尽管你需要创建一个字段名:从评论来看,听起来pydantic团队想让它与非字段验证器一起工作,并使访问
info.field_name
成为可能,所以希望能实现。我会更新这个答案时发生的变化,但检查该链接的情况下,我错过了它。