我有一个代表持有交易的银行账户的 Pydantic 模型,它们本身就是嵌套在账户模型上的模型。交易保存在隐藏字段的列表中,并通过在 setter 方法中应用一些检查的计算字段进行访问。
简化模型:
class Account(BaseModel):
id: UUID = Field(default_factory=uuid4)
owner: Customer
_transactions: List[Transaction] = list()
@fields.computed_field()
@property
def transactions(self) -> List[Transaction]:
return self._transactions
@transactions.setter
def transactions(self, list_: List[Transaction]) -> None:
# DO SOME CHECKS
self._transactions = sorted_list
class Transaction(BaseModel):
id: UUID = Field(default_factory=uuid4)
date: datetime.date = Field(frozen=False)
amount: PositiveFloat = Field(frozen=False)
class Customer(BaseModel):
id: UUID = Field(default_factory=uuid4)
name: datetime.date = Field(frozen=False)
连载
我可以成功将帐户序列化为 JSON,包括交易:
x = my_account.model_dump_json()
x
格式化输出:
'{
"id":"1eeed00b-3bd7-48fa-ab6a-6979618ef723",
"owner":{
"id":"0cad242dad03-492b-9c6b-86f0b75f9c00",
"name":"Bob"
},
"transactions":[
{"id":"42738f1d-e998-4add-94b8-713afe25b525","date":"2012-01-01","amount":1.0}
]
}'
反序列化(意外行为)
但是,当我尝试从 JSON 字符串重建模型时,交易字段为空。拥有嵌套客户模型的所有者字段没有问题:
Account.model_validate_json(x)
格式化输出:
Account(
id=UUID('1eeed00b-3bd7-48fa-ab6a-6979618ef723'),
owner=Customer(
id=UUID('0cad242d-ad03-492b-9c6b-86f0b75f9c00'),
name='Bob'
),
transactions=[]
)
有没有办法使计算字段的序列化过程可逆?
您可以使用模型验证器来做到这一点:
@model_validator(mode="before")
@classmethod
def validate_model(self, data: Any):
if isinstance(data, dict):
self.transactions = data.get("transactions", [])
return data
完整代码示例:
import datetime
from typing import Any, List
from uuid import UUID, uuid4
from pydantic import (BaseModel, Field, PositiveFloat, computed_field,
model_validator)
class Transaction(BaseModel):
id: UUID = Field(default_factory=uuid4)
date: datetime.date = Field(frozen=False)
amount: PositiveFloat = Field(frozen=False)
class Customer(BaseModel):
id: UUID = Field(default_factory=uuid4)
name: str = Field(frozen=False)
class Account(BaseModel):
id: UUID = Field(default_factory=uuid4)
owner: Customer
_transactions: List[Transaction] = list()
@model_validator(mode="before")
@classmethod
def validate_model(self, data: Any):
if isinstance(data, dict):
self.transactions = data.get("transactions", [])
return data
@computed_field()
@property
def transactions(self) -> List[Transaction]:
return self._transactions
@transactions.setter
def transactions(self, list_: List[Transaction]) -> None:
# DO SOME CHECKS
self._transactions = sorted(list_, key=lambda a, b: a.amount>b.amount)
a = Account.model_validate({
"id": uuid4().hex,
"owner":{
"id":uuid4().hex,
"name":"Bob"
},
"transactions":[
{"id":uuid4().hex,"date":"2012-01-01","amount":1.0}
]
})
print(a.model_dump())
输出:
{'id': UUID('0f7f32a1-b3b9-40e8-a35d-e662e5e4032e'), 'owner': {'id': UUID('bca37eaa-086a-4adf-b51c-e05feb812e9b'), 'name': 'Bob'}, 'transactions': [{'id': '87042f4e738a4b24888e6c126260edc3', 'date': '2012-01-01', 'amount': 1.0}]}