# pydantic-数据模型定义与验证

# 概述

Pydantic是一个Python库,可以用于数据验证和数据转换。它提供了一个数据模型声明的方式,可以用来定义数据模型,然后可以使用这些数据模型来验证输入数据的类型和格式,并转换成Python对象。Pydantic还支持自动文档生成和API交互,是一个非常实用的工具。

数据模型声明可以通过定义Python类来实现,其中每个属性都可以指定类型、默认值、描述等属性。当创建类实例时,如果提供的数据与模型不匹配,则会抛出异常,这可以用来快速捕捉输入数据中的错误。Pydantic还提供了很多内置的验证器,比如长度、范围和正则表达式等。

另外,Pydantic还支持自动转换数据类型,可以将JSON、yaml等格式的数据转换成Python对象,也可以将Python对象转换成其他数据格式,比如JSON、msgpack等。

总之,Pydantic是一个非常实用的Python库,可以在许多场景中提高开发效率,比如Web开发、数据处理等。

# pydantic高级特性

# 1. 支持类型转换

Pydantic可以自动进行类型转换,比如将字符串转换成数字或日期。只需要在模型中指定合适的类型即可,Pydantic会自动将传入的数据转换成相应的类型。例如:

from datetime import date
from pydantic import BaseModel

class User(BaseModel):
    id: int
    name: str
    birthdate: date
  
user_data = {'id': 1, 'name': 'John', 'birthdate': '2003-09-11'}
user = User(**user_data)
# 等价于
user = User(id=1, name='John', birthdate=date(2003, 9, 11))

# 2. 支持嵌套模型

Pydantic支持嵌套的模型定义,可以用来描述复杂对象,具有很高的灵活性。例如:

from pydantic import BaseModel
from typing import List

class Item(BaseModel):
    id: int
    name: str
    price: float

class Order(BaseModel):
    id: int
    items: List[Item]
    total: float

order_data = {'id': 1, 'items': [{'id': 1, 'name': 'apple', 'price': 2.0},
                                 {'id': 2, 'name': 'banana', 'price': 1.5}],
              'total': 3.5}
order = Order(**order_data)
# 等价于
order = Order(id=1, items=[Item(id=1, name='apple', price=2.0),
                           Item(id=2, name='banana', price=1.5)],
              total=3.5)

# 3. 支持运算符重载

Pydantic支持运算符重载,可以将Pydantic对象视为普通对象来进行操作。例如:

from pydantic import BaseModel

class Point(BaseModel):
    x: float
    y: float
  
    def __add__(self, other):
        return Point(x=self.x+other.x, y=self.y+other.y)
  
p1 = Point(x=1, y=2)
p2 = Point(x=3, y=4)
p3 = p1 + p2
# p3 = Point(x=4.0, y=6.0)

# 4. 支持参数注释

Pydantic支持在模型中添加参数注释,可以用于自动生成API文档或其他文档。例如:

from pydantic import BaseModel

class User(BaseModel):
    id: int
    name: str = 'anonymous' # 用户名,默认为'anonymous'。
    age: int = None # 年龄,默认为None。

# 使用示例

from pydantic import BaseModel, validator
from typing import Optional, List
from fastapi import FastAPI
from pydantic import BaseModel

app = FastAPI()

class User(BaseModel):
    id: int
    username: str
    password: Optional[str] = None

    @validator('id')
    def id_must_be_positive(cls, v):
        """
        自定义数据校验逻辑:ID必须为正整数
        """
        if v <= 0:
            raise ValueError('ID must be a positive integer')
        return v

@app.post("/add_user")
async def add_user(user_data: List[User]):
    # 验证输入数据是否符合模型定义
    for user in user_data:
        if not isinstance(user.id, int):
            return {"success": False, "message": "ID must be an integer."}
        if not isinstance(user.username, str):
            return {"success": False, "message": "Username must be a string."}
        if not isinstance(user.password, str):
            return {"success": False, "message": "Password must be a string."}

    # 将输入数据转换成Pydantic对象
    users = [User(**user_dict) for user_dict in user_data]

    # 处理业务逻辑
    # ...

    return {"success": True, "message": "User added successfully."}

在上面的示例中,我们给password​字段添加了一个默认值None​,意味着在创建User​实例时,如果不显式指定password​,则该字段的值将会是None​。

我们还在id​字段上定义了自定义验证器id_must_be_positive​,用于检查ID是否为正整数。该验证器使用了validator​装饰器,传入参数是需要进行验证的字段的名称id​。验证函数的参数有两个,一个是被验证的类,另一个是被验证的值v​。函数可以抛出异常来报告校验失败,或者返回值将会被认为是校验后新的值。

最后,我们需要注意到username​字段并没有添加任何校验规则,也没有设置默认值。这意味着在创建User​对象时,如果缺少username​字段,将会抛出ValidationError​异常。如果你想要username​字段不是必需的,可以使用Optional​类型装饰器,如上所示。

综上所述,Pydantic提供了灵活而强大的数据验证机制,通过使用自定义验证函数和装饰器,我们可以针对需要的字段和情景很方便地定义校验规则。