I'm trying to reference the length of one field as a default value for another field in the same class in Pydantic, but not sure how to achieve it.
In this particular case, I want the payload_length
to give me the length of the payload_body
so that it fails validation if the length is greater than 250 bytes.
However, python is telling me that payload_body
is not defined because it's part of the same pydantic class.
Any suggestions?
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()}")
The answer was provided by Gino Mempin .
Using Pydantics @root_validator
worked. Here's the full solution:
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()}")
Result:
2022-12-31 11:18:51:881 ERROR | Payload length cannot exceed 250 bytes. Payload length: 260 bytes.
Switching downlink = Downlink(payload_body="*" * 260)
to downlink = Downlink(payload_body="*" * 10)
passes validation:
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}
try using root_validator
with pre
keyword argument to compute length of 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
output:
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)
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.