如何从同一个类中的字段引用另一个Pydantic字段?



我试图引用一个字段的长度作为Pydantic中同一类中另一个字段的默认值,但不确定如何实现它。

在本例中,我希望payload_length给出payload_body的长度,这样如果长度大于250字节,则验证失败。

然而,python告诉我payload_body没有定义,因为它是同一个pydantic类的一部分。

有什么建议吗?

class Downlink(BaseModel):
payload_id: str = Field(
default_factory=lambda: str(uuid4()),
repr=False,
exclude=False
)
payload_body: str = Field(
repr=True
)
payload_length: int = Field(
default_factory=lambda: len(payload_body),
le=250,
repr=True
)
created_at: str = Field(
default_factory=lambda: str(datetime.datetime.now()),
)

downlink = Downlink(payload_body="This is a test of a long Downlink message that should be less than 250 bytes in length.")
logger.debug(f"Downlink created. {downlink.dict()}")

Gino Mempin的评论提供了答案。

它不是声明性的,但您可以使用根验证器:https://docs.pydantic.dev/usage/validators/#root-validators

使用Pydantic的@root_validator有效。

完整的解决方案如下:

class Downlink(BaseModel):
payload_id: UUID = Field(
default_factory=uuid4,
)
payload_body: str = Field(
repr=True
)
created_at: datetime = Field(
default_factory=datetime.now
)
_payload_length: int = Field(
ge=1,
le=250,
)
## Function to automatically generate the payload_length field
@root_validator
def get_payload_length(cls, values):
length = len(values['payload_body'])
if length > 250:
logger.error(f"Payload length cannot exceed 250 bytes. Payload length: {length} bytes.")
raise ValueError("Payload length cannot exceed 250 bytes.")
else:
values['_payload_length'] = length
return values

downlink = Downlink(payload_body="*" * 260)
logger.debug(f"Downlink created. {downlink.dict()}")

结果:

2022-12-31 11:18:51:881 ERROR | Payload length cannot exceed 250 bytes. Payload length: 260 bytes.

切换
downlink = Downlink(payload_body="*" * 260)

downlink = Downlink(payload_body="*" * 10)

通过验证:

2022-12-31 11:22:13:522 DEBUG | Downlink created. {'payload_id': UUID('f0bd0126-f855-4d2e-9b01-f6dc32c05932'), 'payload_body': '**********', 'created_at': datetime.datetime(2022, 12, 31, 11, 22, 13, 522130), '_payload_length': 10}

尝试使用root_validatorpre关键字参数来计算payload_body的长度。

import datetime
import typing
from uuid import uuid4
from pydantic import BaseModel, ValidationError, root_validator

class Downlink(BaseModel):
payload_id: str = Field(
default_factory=lambda: str(uuid4()), repr=False, exclude=False
)
payload_body: str = Field(repr=True)
payload_length: int = Field(default=0, le=250, repr=True)
created_at: str = Field(
default_factory=lambda: str(datetime.datetime.now()),
)
@root_validator(pre=True)
@classmethod
def set_payload_length(cls, values: typing.Dict):
if values.get("payload_body", ""):
values.update({"payload_length": len(values["payload_body"])})
return values

downlink = Downlink(
payload_body="This is a test of a long Downlink message that should be less than 250 bytes in length."
)
print(f"Downlink created. {downlink.dict()}")
try:
downlink = Downlink(
payload_body="This is a test of a long Downlink message that should be less than 250 bytes in length."
* 10
)
except ValidationError as e:
print(f"An error should occur: {e=}")
raise e

输出:

Downlink created. {'payload_id': '8feb02b2-f28e-4f2b-b75f-0bd4bde887bb', 'payload_body': 'This is a test of a long Downlink message that should be less than 250 bytes in length.', 'payload_length': 87, 'created_at': '2023-01-02 17:41:12.693928'}
pydantic.error_wrappers.ValidationError: 1 validation error for Downlink
payload_length
ensure this value is less than or equal to 250 (type=value_error.number.not_le; limit_value=250)