如何在同一Pydantic类中引用其他字段的值?
如何在同一Pydantic类中引用其他字段的值?
这问题我碰到过好多次啦,咱们一步步来解决~
首先得说清楚你原来的代码为啥报错:在定义payload_length的default_factory时,payload_body还没被实例化呢——Pydantic类在定义阶段,字段都是类属性,还没变成实例的属性,所以lambda里根本找不到payload_body这个变量,Python自然会报错。
接下来给你两种靠谱的解决方案,分Pydantic版本来说,你根据自己用的版本选就行:
方案一:Pydantic v2 用computed_field(推荐)
Pydantic v2新增了computed_field装饰器,专门用来处理这种依赖其他字段的派生值,代码写起来特别清爽。而且正好能满足你“验证字节长度不超过250”的需求:
import uuid import datetime from pydantic import BaseModel, Field, computed_field, ValueError class Downlink(BaseModel): payload_id: str = Field( default_factory=lambda: str(uuid.uuid4()), repr=False, exclude=False ) payload_body: str = Field(repr=True) created_at: str = Field( default_factory=lambda: str(datetime.datetime.now()), ) @computed_field(return_type=int, repr=True) def payload_length(self) -> int: # 注意:你要的是字节长度,不是字符数,所以得先encode成字节串再算长度 byte_length = len(self.payload_body.encode('utf-8')) if byte_length > 250: raise ValueError("payload_body的字节长度不能超过250") return byte_length # 测试一下 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()}")
这里的关键点:
@computed_field会自动把这个方法的返回值作为payload_length的值,不用手动赋值- 我特意把
len(payload_body)改成了len(payload_body.encode('utf-8'))——因为你要求的是字节长度,直接用len()算的是字符数,比如中文每个字符占3个字节,会和你预期的不一致,这点一定要注意! - 如果字节长度超过250,会直接抛出验证错误,符合你的需求
方案二:用模型验证器(Pydantic v2/v1都能用)
如果你的项目还在v1,或者需要更灵活的逻辑,可以用验证器来设置payload_length的值,同时完成验证:
Pydantic v2版本
import uuid import datetime from pydantic import BaseModel, Field, model_validator class Downlink(BaseModel): payload_id: str = Field( default_factory=lambda: str(uuid.uuid4()), repr=False, exclude=False ) payload_body: str = Field(repr=True) payload_length: int = Field(le=250, repr=True) # 直接在这里加长度限制 created_at: str = Field( default_factory=lambda: str(datetime.datetime.now()), ) @model_validator(mode='before') @classmethod def set_payload_length(cls, values): # 在验证前先计算payload_length的值 if 'payload_body' in values: values['payload_length'] = len(values['payload_body'].encode('utf-8')) return values
Pydantic v1版本
import uuid import datetime from pydantic import BaseModel, Field, validator class Downlink(BaseModel): payload_id: str = Field( default_factory=lambda: str(uuid.uuid4()), repr=False, exclude=False ) payload_body: str = Field(repr=True) payload_length: int = Field(le=250, repr=True) created_at: str = Field( default_factory=lambda: str(datetime.datetime.now()), ) @validator('payload_length', always=True) def set_payload_length(cls, v, values): # always=True表示不管有没有传入payload_length,都会执行这个验证器 if 'payload_body' in values: return len(values['payload_body'].encode('utf-8')) return v
这种方法的逻辑是:在模型验证的阶段,先把payload_length的值设置为payload_body的字节长度,然后Pydantic会自动用le=250验证这个值是否符合要求,超过的话直接抛出验证错误。
最后再提醒你一句:如果你的payload_body可能包含非UTF-8编码的内容,记得把encode('utf-8')改成你实际用的编码哦!
备注:内容来源于stack exchange,提问作者Don T Spamme
相关产品推荐
相关产品推荐

