数据模型定义:使用 Python 类来定义数据模型,这些类可以自动将输入转换为 Python 数据类型。
from pydantic import BaseModel
class User(BaseModel):
id: int
name: str
email: str
类型检查:Pydantic 强制执行类型检查,确保传入的数据符合模型定义的类型。
user = User(id=1, name='John Doe', email='john.doe@example.com')
# 如果传递错误的类型,比如将id作为字符串,将会引发错误
自动转换:Pydantic 可以将字符串输入自动转换为 Python 原生类型,如 int, float, bool, datetime 等。
class User(BaseModel):
id: int = Field(..., alias='user_id') # 使用别名
email: str
user = User(user_id='123', email='john.doe@example.com')
print(user.id) # 输出: 123
错误处理:当输入数据不符合模型定义时,Pydantic 提供了详细的错误信息,方便调试。
try:
User(id='invalid', name='John Doe', email='john.doe@example.com')
except ValidationError as e:
print(e.json())
配置选项:Pydantic 允许通过配置选项来自定义模型的行为,如是否允许额外字段,是否进行严格的类型检查等。
from pydantic import BaseModel, Extra, Field
# 定义一个模型
class User(BaseModel):
id: int
name: str
email: str
# 内部类 Config 用于设置配置选项
class Config:
# 指定如何处理模型中未定义的额外字段
extra = Extra.allow # 允许额外字段,它们将被存储在模型的 additional 字典中
# 指定是否允许修改模型实例的属性
allow_mutation = False # 不允许修改,使模型实例成为不可变的
# 指定是否将 JSON 字段名作为属性名,而不是 Python 变量名
alias_generator = lambda x: x # 使用字段的 JSON 名称作为属性名
# 指定是否将带下划线的属性视为私有
underscore_attrs_are_private = True # 将带下划线的属性视为私有
# 指定是否在模型初始化时进行严格的类型检查
strict_types = True # 开启严格类型检查
# 指定是否允许使用任意类型作为字段类型
arbitrary_types_allowed = False # 不允许任意类型
# 指定是否将枚举的值用作 JSON 序列化
use_enum_values = True # 使用枚举值而不是枚举的名称
# 指定 JSON 编码器
json_encoders = {
int: lambda x: str(x) # 为整数定义自定义 JSON 编码器
}
# 为 JSON Schema 指定额外的字段
json_schema_extra = {
'example': {'id': 1, 'name': 'Alice', 'email': 'alice@example.com'} # 示例数据
}
泛型模型:支持泛型模型,可以创建基于泛型的复杂数据结构。
from typing import List, Dict
class ModelGenerics(BaseModel):
data: List[str]
config: Dict[str, str]
data = ModelGenerics(data=['item1', 'item2'], config={'key': 'value'})
在 Python 3.5 中引入,作为对泛型的支持。
在 Python 的 typing 模块中,Optional 和 Union 是两个非常有用的类型注解工具,它们经常一起使用来表达某些类型上的需求。
Optional
Optional 是一个用于表示某个类型的值可能存在,也可能是 None 的类型注解。它通常用于函数注解中,来表明一个参数可以被省略(默认为 None),或者一个变量可能有值或没有值。
Union
Union 是一个类型注解工具,它允许一个变量可以是多种类型中的一种。它使用圆括号 () 来包裹多个类型,表示变量可以是这些类型中的任何一个。
Optional[Union[int, str]]
当你看到 Optional[Union[int, str]] 这样的类型注解时,它的意思是:
- 这个注解表示的变量可以接受以下三种情况之一:
- 一个整数 (int)
- 一个字符串 (str)
- 没有值,即 None
在函数注解中,这通常表示一个参数可以是整数、字符串或者完全不传(默认为 None)。
下面是一个使用 Optional[Union[int, str]] 的例子:
from typing import Optional, Union
def process_value(value: Optional[Union[int, str]]) -> str:
if value is None:
return "No value provided"
elif isinstance(value, int):
return f"The value is an integer: {value}"
elif isinstance(value, str):
return f"The value is a string: {value}"
else:
return "Unknown type"
Callable
在 Python 中,Callable 是 typing 模块提供的一个泛型类型,用于注解函数对象。当你想要定义一个变量、参数或者返回值必须是一个函数时,可以使用 Callable。
Callable 的使用格式如下:
Callable[[arg1_type, arg2_type, ..., argN_type], return_type]
- arg1_type, arg2_type, …, argN_type 是函数参数的类型。
- return_type 是函数返回值的类型。
JSON 支持:Pydantic 可以轻松地将模型序列化为 JSON,也可以从 JSON 反序列化模型。
序列化为 JSON
user = User(id=1, name='John Doe', email='john.doe@example.com')
user_json = user.json() # 序列化为JSON字符串
print(user_json)
JSON 反序列化模型
'{
"id": 1,
"name": "John Doe",
"email": "john.doe@example.com"
}'
user_json = '''
{
"id": 1,
"name": "John Doe",
"email": "john.doe@example.com"
}
'''
user = User.parse_raw(user_json)
print(user)
print(user.id, user.name, user.email)
环境变量加载:Pydantic 可以自动从环境变量中加载配置。
from pydantic import BaseSettings
# 定义一个继承自 BaseSettings 的 Settings 类
class Settings(BaseSettings):
# 定义一个名为 DEBUG 的布尔类型配置项,并设置默认值为 False
DEBUG: bool = False
# 定义一个名为 DB_USER 的字符串类型配置项,没有默认值,需要从环境变量或其它方式提供
DB_USER: str
# 定义一个名为 DB_PASSWORD 的字符串类型配置项,同样需要外部提供
DB_PASSWORD: str
# 创建 Settings 类的实例
settings = Settings()
# 打印 DEBUG 配置项的值,由于没有指定环境变量或其它赋值方式,将输出默认值 False
print(settings.DEBUG)
# 尝试打印 DB_USER 配置项的值
# 如果没有通过环境变量或其它方式提供 DB_USER 的值,这将抛出一个错误,因为没有默认值
print(settings.DB_USER)
字段校验:提供了多种字段校验器,用于验证数据的有效性,如长度、正则表达式匹配等。
from pydantic import BaseModel, Field, validator
from typing import List
class User(BaseModel):
name: str
email: str
passwords: List[str] = Field(..., min_items=2)
@validator('email')
def email_must_contain_at(cls, v):
if '@' not in v:
raise ValueError('must contain @')
return v
user = User(name='John', email='john.doe', passwords=['pass1', 'pass2'])
pydantic 与JSON Schema 关系与关系
JSON Schema 是一种基于 JSON 格式的声明性语言,用于描述和验证 JSON 数据结构。它最初由 JSON 的创建者 Douglas Crockford 提出概念,并由一个社区驱动的进程进一步开发和标准化。JSON Schema 的目的是为了让 JSON 数据能够自我描述,即包含足够的元数据来描述其自身的结构,从而允许自动化的验证和编辑。
JSON Schema 的主要用途包括:
- 数据验证:确保 JSON 数据符合预定义的模式,比如检查数据类型、格式、长度等。
- 自动化测试:自动化测试工具可以使用 JSON Schema 来验证 API 返回的数据是否符合预期。
- API 文档:作为 API 文档的一部分,帮助开发者理解 API 请求和响应的数据结构。
- 配置管理:用于定义配置文件的结构,确保配置文件中的设置是有效和合理的。
- 用户界面生成:基于 JSON Schema 自动生成表单和用户界面,以输入和编辑数据。
- 数据交换:作为不同系统或组件之间交换数据时的契约,确保数据的兼容性。
JSON Schema 被广泛应用于各种编程语言和环境中,是现代软件开发中数据验证和描述的重要工具之一。随着 JSON 格式的普及,JSON Schema 也成为了描述 JSON 数据的事实标准之一。
要将 Pydantic 模型转换为 JSON Schema,你可以使用模型的 schema()
方法。下面是一个示例:
from pydantic import BaseModel, Field
class User(BaseModel):
id: int
name: str
email: str
# 定义一个字段的额外信息,比如示例值
email = Field(..., example='user@example.com')
# 获取 JSON Schema 表示
schema = User.schema()
print(schema)
运行上述代码会输出类似于下面的 JSON Schema:
{
"$schema": "http://json-schema.org/draft-07/schema#",
"title": "User",
"type": "object",
"properties": {
"id": {
"type": "integer"
},
"name": {
"type": "string"
},
"email": {
"type": "string",
"format": "email"
}
},
"required": [
"id",
"name",
"email"
],
"example": {
"id": 0,
"name": "",
"email": "user@example.com"
}
}
请注意,输出的 JSON Schema 包含了模型的所有字段,它们的类型,以及是否是必须的。此外,如果字段使用了 Field
函数来定义额外的元数据(如示例值),这些信息也会被包含在内。
JSON Schema 可以用于各种用途,包括:
- 自动化测试:验证输入数据是否符合预期的模型。
- API 文档:为 API 提供正式的、可机器读取的文档。
- 代码生成:基于 JSON Schema 自动生成代码,如客户端库或服务端存根。
- 交互式表单:基于 JSON Schema 自动生成表单界面。
Pydantic 的 schema()
方法是一个非常强大的工具,可以帮助你将 Python 模型定义转换为一个广泛接受的、标准化的格式。
Pydantic 支持嵌套模型,这意味着你可以在一个 Pydantic 模型中定义另一个 Pydantic 模型作为其字段之一。这使得你可以创建复杂的数据结构,如对象包含对象,或者对象包含数组等。
嵌套模型示例
假设我们有一个博客系统,需要定义 Post
和 User
两个模型,其中 Post
模型嵌套了 User
模型:
from pydantic import BaseModel
class User(BaseModel):
name: str
email: str
class Post(BaseModel):
title: str
content: str
author: User # 这里 Post 嵌套了 User 模型作为其字段
published: bool = False
# 创建 Post 实例
post = Post(
title='Pydantic 嵌套模型',
content='Pydantic 是多么方便啊!',
author=User(name='Alice', email='alice@example.com'),
published=True
)
print(post)
在这个例子中:
User
是一个包含name
和email
字段的 Pydantic 模型。Post
是另一个 Pydantic 模型,它有一个author
字段,该字段的类型是User
。这样,Post
模型就嵌套了User
模型。- 创建
Post
实例时,你需要为author
字段提供一个User
实例。
嵌套模型的 JSON Schema
当你为嵌套的 Pydantic 模型生成 JSON Schema 时,Pydantic 会自动处理这些嵌套关系,生成相应的 schema:
print(Post.schema())
这将输出一个包含 Post
和 User
模型的 JSON Schema,其中 Post
的 author
字段会链接到 User
的 schema。
嵌套模型的序列化和反序列化
Pydantic 同样支持嵌套模型的序列化(将模型实例转换为 JSON)和反序列化(从 JSON 创建模型实例):
# 序列化 Post 实例为 JSON
post_json = post.json()
print(post_json)
# 假设有一段 JSON 字符串表示 Post
post_json_str = '''
{
"title": "Pydantic 嵌套模型",
"content": "Pydantic 是多么方便啊!",
"author": {
"name": "Alice",
"email": "alice@example.com"
},
"published": true
}
'''
# 反序列化 JSON 字符串为 Post 实例
post_from_json = Post.parse_raw(post_json_str)
print(post_from_json)
通过嵌套模型,Pydantic 提供了一种强大的方式来处理复杂的数据结构,同时保持了序列化和反序列化的便利性。