在构建现代 Web 应用时,我们往往需要处理复杂的输入和输出数据结构。例如,响应数据可能包含嵌套字典、列表、元组,甚至是多个嵌套对象。Pydantic 是一个强大的数据验证和序列化库,可以帮助我们轻松地处理这些复杂的数据结构,并通过自定义方法进行验证和转换。
本文将介绍如何使用 Pydantic 处理复杂数据结构,包括嵌套模型、嵌套字典、列表、元组等,及如何使用自定义方法进行数据验证。
1. Pydantic 简介
Pydantic 通过定义 Python 类并继承 BaseModel
,使得开发者能够轻松定义数据模型并进行自动验证。Pydantic 支持多种数据类型,包括基本类型(如 int
、str
等)和更复杂的类型(如 List
、Dict
、Tuple
、嵌套模型等)。
2. 嵌套模型
2.1 嵌套模型的定义
在许多应用场景中,数据往往具有层级结构。例如,一个订单可能包含多个商品项,每个商品项都有自己的名称、数量和价格。我们可以通过嵌套 Pydantic 模型来处理这种层级结构。
假设我们有以下数据结构:一个订单包含用户信息和多个商品项。我们可以定义两个模型,User
和 Item
,并在 Order
模型中嵌套这两个模型。
from pydantic import BaseModel
from typing import Listclass Item(BaseModel):name: strquantity: intprice: floatclass User(BaseModel):name: stremail: strclass Order(BaseModel):user: Useritems: List[Item]total_amount: float
在这个例子中:
Order
模型嵌套了User
和Item
模型。items
字段是一个Item
对象的列表,表示订单中的多个商品项。total_amount
字段表示订单总金额。
2.2 使用嵌套模型
我们可以像下面这样创建一个订单对象:
order_data = {"user": {"name": "John Doe", "email": "john.doe@example.com"},"items": [{"name": "Laptop", "quantity": 1, "price": 1200.00},{"name": "Mouse", "quantity": 2, "price": 25.50}],"total_amount": 1251.00
}order = Order(**order_data)
print(order)
输出将是:
user=User(name='John Doe', email='john.doe@example.com')
items=[Item(name='Laptop', quantity=1, price=1200.0), Item(name='Mouse', quantity=2, price=25.5)]
total_amount=1251.0
2.3 嵌套字典和列表
Pydantic 模型也可以处理嵌套字典和列表结构。假设我们有一个场景,其中每个商品项可能包含多个属性,如商品的属性信息。
from typing import Dictclass Item(BaseModel):name: strquantity: intprice: floatattributes: Dict[str, str] # 商品的额外属性class Order(BaseModel):user: Useritems: List[Item]total_amount: float
在这种情况下,attributes
字段是一个字典,存储商品的属性信息,如颜色、尺寸等。
order_data = {"user": {"name": "John Doe", "email": "john.doe@example.com"},"items": [{"name": "Laptop", "quantity": 1, "price": 1200.00, "attributes": {"color": "black", "size": "15 inch"}},{"name": "Mouse", "quantity": 2, "price": 25.50, "attributes": {"color": "red", "wireless": "yes"}}],"total_amount": 1251.00
}order = Order(**order_data)
print(order)
2.4 处理元组和其他数据类型
Pydantic 同样支持验证元组、集合等数据类型。我们可以使用 Tuple
来验证数据。
from typing import Tupleclass Order(BaseModel):user: Useritems: List[Item]total_amount: floatstatus: Tuple[str, str] # 状态元组:订单状态和配送状态
在上面的代码中,status
是一个元组,包含两个字符串,分别表示订单的状态和配送状态。
3. 数据验证的自定义方法
Pydantic 允许我们为模型字段添加自定义验证方法,这使得我们可以根据特定规则对数据进行进一步验证。
3.1 使用 @validator
装饰器进行字段验证
假设我们需要验证订单总金额 total_amount
,确保它不小于所有商品项的总价格。我们可以使用 @root_validator
装饰器来实现这个逻辑。
from pydantic import root_validator, ValidationErrorclass Order(BaseModel):user: Useritems: List[Item]total_amount: float@root_validatordef check_total_amount(cls, values):items = values.get('items')total_amount = values.get('total_amount')if items and total_amount:total_price = sum(item.quantity * item.price for item in items)if total_amount < total_price:raise ValueError('Total amount cannot be less than the sum of item prices.')return values
3.2 示例验证
假设我们创建一个订单,其中 total_amount
小于所有商品项的总价格:
order_data = {"user": {"name": "John Doe", "email": "john.doe@example.com"},"items": [{"name": "Laptop", "quantity": 1, "price": 1200.00},{"name": "Mouse", "quantity": 2, "price": 25.50}],"total_amount": 1000.00
}try:order = Order(**order_data)
except ValidationError as e:print(e)
输出将是:
1 validation error for Order
__root__Total amount cannot be less than the sum of item prices. (type=value_error)
Pydantic 会自动执行这个验证,并返回详细的错误信息。
3.3 验证嵌套模型中的数据
你还可以为嵌套的模型添加自定义验证。例如,我们可以确保用户的邮箱地址包含 @
符号:
class User(BaseModel):name: stremail: str@validator('email')def validate_email(cls, value):if '@' not in value:raise ValueError('Email must contain "@" symbol')return value
这样,如果用户的邮箱地址没有 @
符号,Pydantic 会自动抛出验证错误。
Pydantic 提供了强大的数据验证功能,帮助开发者轻松处理复杂的输入和输出数据结构。通过嵌套模型、字典、列表、元组等类型的支持,Pydantic 使得数据处理更加灵活和易于管理。同时,自定义的验证方法(如 @validator
和 @root_validator
)允许开发者根据业务逻辑定制数据验证规则,确保数据的正确性和一致性。
在使用 Pydantic 时,通过合理的模型设计和验证方法,可以提高代码的可读性、可维护性和健壮性。如果你正在构建需要复杂数据结构验证的应用,Pydantic 是一个非常值得依赖的工具。