FastAPI:(4)请求体
概述
文章内容
graph TD
%% 核心概念
A[请求体] -->|结构化数据载体| B[Body请求体]
A -->|字段控制| C[Field]
A -->|复杂结构| D[嵌套模型]
A -->|部分更新| E[参数更新]
%% Body请求体分支
B --> B1["单一值处理"]
B --> B2["多参数组合"]
B --> B3["JSON自动解析"]
%% Field分支
C --> C1["字段校验"]
C --> C2["元数据描述"]
C --> C3["文档生成"]
%% 嵌套模型分支
D --> D1["层次化结构"]
D --> D2["递归验证"]
D --> D3["类型安全"]
D -->|支持| D4[子类型]
D4 --> D4a["Union/List/Dict"]
D4 --> D4b["自定义子类"]
%% 参数更新分支
E --> E1["PATCH部分更新"]
E --> E2["PUT完整替换"]
E --> E3["exclude_unset过滤"]
E -->|依赖| C[Field]
1.请求体与多参数
请求体
请求体(Request Body)是指在 HTTP 请求中携带的、用于向服务端传递结构化数据的一部分,通常用于 POST、PUT、PATCH、DELETE 等方法中。
在 FastAPI 中,请求体通过 Pydantic 模型或 Body() 函数来声明,框架会自动解析请求数据、校验格式、生成文档,并将其作为函数参数传递。
重要特征:
- 结构化:请求体支持 JSON 等格式,必须具备明确的字段结构,通常通过 Pydantic 模型进行声明。
- 自动解析与验证:FastAPI 会根据类型提示自动解析数据并校验类型与格式。
- 文档自动生成:基于请求体模型,自动生成 API 文档中的 Schema。
- 与参数位置有关:请求体不同于路径参数(Path)和查询参数(Query),是 HTTP 请求的一部分,不暴露于 URL。
- 可以嵌套与复用:支持嵌套结构与模型组合,方便表达复杂数据结构。
电商商品发布功能
请求体
现象:
一个电商平台允许商家发布商品,前端发送包含商品名称、价格、描述、标签等字段的 JSON 数据,通过 POST /items/ 接口提交。服务器端使用 Pydantic 模型定义这些字段,确保名称为字符串、价格为浮点数、标签为字符串列表,数据校验通过后写入数据库。
特征对比
- 结构化 ✔:数据为 JSON 格式,结构清晰
- 自动验证 ✔:Pydantic 校验字段类型和必填性
- 文档生成 ✔:自动生成请求体结构的 API 文档
- 请求体作用明确 ✔:传输复杂结构数据,不适合用查询参数
代码例子
请求体
仅使用 Python 类型声明,FastAPI 就可以:
- 以 JSON 形式读取请求体
- (在必要时)把请求体转换为对应的类型
- 校验数据:
- 数据无效时返回错误信息,并指出错误数据的确切位置和内容
- 把接收的数据赋值给参数
item- 把函数中请求体参数的类型声明为
Item,还能获得代码补全等编辑器支持
- 把函数中请求体参数的类型声明为
- 为模型生成 JSON Schema,在项目中所需的位置使用
- 这些概图是 OpenAPI 概图的部件,用于 API 文档 UI
from fastapi import FastAPI
from pydantic import BaseModel # 从pydantic中导入BaseModel
class Item(BaseModel): # 创建数据模型,包含默认值,默认值为None的为可选参数,没有默认值为必选参数
name: str
description: str | None = None
price: float
tax: float | None = None
app = FastAPI()
@app.post("/items/")
async def create_item(item: Item): # 声明请求体参数
item_dict = item.dict()
if item.tax is not None:
price_with_tax = item.price + item.tax # 使用模型
item_dict.update({"price_with_tax": price_with_tax})
return item_dict
请求+路劲+查询参数
请求体
[[FastAPI:(3)参数]]
FastAPI 支持同时声明请求体、路径参数和查询参数。能够正确识别这三种参数,并从正确的位置获取数据。
- 路径中声的参数
{item_id},是路径参数 - 类型是(
int、float、str、bool等)单类型的参数,是查询参数 - 类型是 Pydantic 模型的参数,是请求体
from fastapi import FastAPI
from pydantic import BaseModel
class Item(BaseModel):
name: str
description: str | None = None
price: float
tax: float | None = None
app = FastAPI()
@app.put("/items/{item_id}")
async def update_item(item_id: int, item: Item, q: str | None = None):
result = {"item_id": item_id, **item.dict()}
if q:
result.update({"q": q})
return result
多个请求体参数
请求体
from fastapi import FastAPI
from pydantic import BaseModel
app = FastAPI()
class Item(BaseModel):
name: str
description: str | None = None
price: float
tax: float | None = None
class User(BaseModel):
username: str
full_name: str | None = None
@app.put("/items/{item_id}")
async def update_item(item_id: int, item: Item, user: User):
results = {"item_id": item_id, "item": item, "user": user}
return results
- FastAPI 将注意到该函数中有多个请求体参数(两个 Pydantic 模型参数)。
- 它将使用参数名称作为请求体中的键(字段名称),并期望一个类似于以下内容的请求体。
- FastAPI 将自动对请求中的数据进行转换,因此
item参数将接收指定的内容,user参数也是如此。 - 它将执行对复合数据的校验,并且像现在这样为 OpenAPI 模式和自动化文档对其进行记录。
{
"item": {
"name": "Foo",
"description": "The pretender",
"price": 42.0,
"tax": 3.2
},
"user": {
"username": "dave",
"full_name": "Dave Grohl"
}
}
Body请求体
在 FastAPI 中,Body 请求(Body Parameters) 指的是通过 HTTP 请求体(Request Body)传递的数据,这些数据通常是结构化格式(如 JSON),用于客户端向服务端提交复杂数据内容,例如用户信息、表单数据、嵌套对象等。
Body 同样具有与 Query、Path 以及其他后面将看到的类完全相同的额外校验和元数据参数。
重要特征:
- 传输结构化数据:用于提交复杂、嵌套的数据结构(如对象、数组)。
- 自动数据解析:FastAPI 自动将 JSON 数据解析为 Python 对象。
- 生成交互式文档:请求体结构会在 Swagger UI 中自动展示。
- 仅适用于特定方法:Body 参数通常用于 POST、PUT、PATCH、DELETE,而不是 GET(GET 请求体通常被忽略)。
- 可与其他参数共用:可与 Path、Query、Header、Cookie 等参数类型同时使用。
请求体单一值
Body请求体
如果想在「请求体」参数中融入一个单一的值,而不是使用pydantic模型时,如果按原样声明它,因为它是一个单一值,FastAPI 将假定它是一个「查询参数」。可以使用 Body 指示 FastAPI 将其作为请求体的另一个键进行处理。
from typing import Annotated
from fastapi import Body, FastAPI
from pydantic import BaseModel
app = FastAPI()
class Item(BaseModel):
name: str
description: str | None = None
price: float
tax: float | None = None
class User(BaseModel):
username: str
full_name: str | None = None
@app.put("/items/{item_id}")
async def update_item(
item_id: int, item: Item, user: User, importance: Annotated[int, Body()]
): # 将importance声明为也给单一的「请求体」值
results = {"item_id": item_id, "item": item, "user": user, "importance": importance}
return results
FastAPI期待的请求体为:
{
"item": {
"name": "Foo",
"description": "The pretender",
"price": 42.0,
"tax": 3.2
},
"user": {
"username": "dave",
"full_name": "Dave Grohl"
},
"importance": 5
}
单个请求体参数
假设你只有一个来自 Pydantic 模型 Item 的请求体参数 item。认情况下,FastAPI 将直接期望这样的请求体。
但是,如果你希望它期望一个拥有 item 键并在值中包含模型内容的 JSON,就像在声明额外的请求体参数时所做的那样,则可以使用一个特殊的 Body 参数 embed:
from typing import Annotated
from fastapi import Body, FastAPI
from pydantic import BaseModel
app = FastAPI()
class Item(BaseModel):
name: str
description: str | None = None
price: float
tax: float | None = None
@app.put("/items/{item_id}")
async def update_item(item_id: int, item: Annotated[Item, Body(embed=True)]):
results = {"item_id": item_id, "item": item}
return results
在这种情况下,FastAPI 将期望像这样的请求体:
{
"item": {
"name": "Foo",
"description": "The pretender",
"price": 42.0,
"tax": 3.2
}
}
2.请求体字段
Field
Field 是 Pydantic 提供的一个函数,用于为模型字段设置额外的元数据与验证规则。它常用于:
- 指定字段的默认值或默认工厂(
default_factory); - 添加验证约束(如最大长度、正则表达式);
- 提供文档描述信息(如
description、title); - 指定字段在自动生成的 API 文档中的行为。
通过 Field,开发者不仅能定义字段的值,还能增强字段的语义性、验证性和文档性。
请求体校验与元数据
Field
- 导入Field,注意,与从
fastapi导入Query,Path、Body不同,要直接从pydantic导入Field。 - 然后,使用
Field定义模型的属性
from typing import Annotated
from fastapi import Body, FastAPI
from pydantic import BaseModel, Field # 导入Field
app = FastAPI()
class Item(BaseModel): # 使用Field定义属性
name: str
description: str | None = Field(
default=None, title="The description of the item", max_length=300
)
price: float = Field(gt=0, description="The price must be greater than zero")
tax: float | None = None
@app.put("/items/{item_id}")
async def update_item(item_id: int, item: Annotated[Item, Body(embed=True)]):
results = {"item_id": item_id, "item": item}
return results
细节
技术细节
Field 的工作方式和 Query、Path、Body 相同,参数也相同。
实际上,
Query、Path都是Params的子类,而Params类又是 Pydantic 中FieldInfo的子类。Pydantic 的
Field返回也是FieldInfo的类实例。
Body直接返回的也是FieldInfo的子类的对象。后文还会介绍一些Body的子类。
注意,从 fastapi 导入的 Query、Path 等对象实际上都是返回特殊类的函数。
3.请求体嵌套模型
子类型
字段可以是复合类型(如 List、Dict)或是多个类型的联合(通过 Union),或者字段本身就是某个父类的子类。例如,某个字段可以接受不同类型的数据,或者字段类型可以是某个类的子类。通过使用这些子类型,可以使数据模型更加灵活,支持不同的输入格式和类型验证。
常见类型及其含义:
Union:表示该字段可以是多个类型中的一种,FastAPI 会根据请求体的实际类型来进行自动推断和验证。
例如,字段可能是整数或字符串,FastAPI 会根据实际输入来决定使用哪种类型。Optional:通常用来表示字段值可以是某个类型,也可以是None(即字段是可选的)。List/Set/Dict:表示字段是列表、集合或字典类型,且每个元素的类型可以是某个子类型。- 自定义子类型:通过继承基础模型类,创建一个新的模型,这个新模型可以作为父模型的子类型使用。
重要特征:
- 灵活性:通过允许字段具有多个类型,可以灵活应对不同的数据输入场景。
- 类型推断:FastAPI 能自动推断字段的数据类型并进行验证。
- 增强文档:字段类型的子类型会自动反映到 Swagger UI 中,生成清晰的 API 文档。
- 支持多种结构:可以使用如
Union等类型,支持多个可能类型的字段。
List与Set
子类型
from typing import List, Union # 导入List
from fastapi import FastAPI
from pydantic import BaseModel
app = FastAPI()
class Item(BaseModel):
name: str
description: Union[str, None] = None
price: float
tax: Union[float, None] = None
tags: List[str] = [] # 声明具有子类型的List,将tag明确指定为一个「字符串列表」
tags: set[str] = set() # 标签不重复,使用set「集合」的子类型
@app.put("/items/{item_id}")
async def update_item(item_id: int, item: Item):
results = {"item_id": item_id, "item": item}
return results
嵌套模型
嵌套模型是指通过Pydantic模型的组合,将一个数据模型作为另一个数据模型的字段类型,从而构建具有层次化结构的复杂数据验证和序列化系统。它允许开发者用面向对象的方式定义复杂的JSON数据结构,并自动获得数据验证、类型检查和API文档生成功能。
重要特征:
- 层次化结构:模型之间存在包含关系,形成树状的数据结构
- 递归验证:每个嵌套层级都会进行独立的数据类型和格式验证
- 类型安全:通过Python类型提示确保编译时和运行时的类型正确性
- 自动序列化:可以在Python对象和JSON之间自动转换
- 文档自生成:自动生成包含所有嵌套结构的OpenAPI规范文档
学生选课系统(正例)
嵌套模型
一个大学的选课系统,学生信息包含个人基本信息、所选课程列表,每门课程又包含课程信息和任课教师信息。
特征对比:
- 层次化结构:✓ 学生→课程→教师,形成三层嵌套结构
- 递归验证:✓ 学生信息、课程信息、教师信息分别验证
- 类型安全:✓ 每个字段都有明确的类型定义
- 自动序列化:✓ 可以直接转换为JSON进行API传输
- 文档自生成:✓ 自动生成完整的API文档说明
简单用户注册表单(反例)
嵌套模型
一个只包含用户名、邮箱、密码的扁平化用户注册表单,所有字段都是基础数据类型。
特征对比:
- 层次化结构:✗ 所有字段都在同一层级,没有嵌套关系
- 递归验证:✗ 只有单层验证,不存在递归验证场景
- 类型安全:✓ 仍然具有基础的类型检查功能
- 自动序列化:✓ 可以序列化,但结构简单
- 文档自生成:✓ 可以生成文档,但结构扁平
List嵌套
嵌套模型
企业系统架构(正例)
嵌套模型
graph TB
%% 定义样式
classDef primaryModel fill:#e1f5fe,stroke:#01579b,stroke-width:3px,color:#000
classDef nestedModel fill:#f3e5f5,stroke:#4a148c,stroke-width:2px,color:#000
classDef enumModel fill:#fff3e0,stroke:#e65100,stroke-width:2px,color:#000
classDef relationship stroke:#2e7d32,stroke-width:2px,color:#2e7d32
%% 主要模型
Department["`**Department 部门**
---
• id: int
• name: str
• description: str
• budget: float
---
**属性方法:**
• total_employees()
• department_salary_cost()`"]:::primaryModel
%% 嵌套模型
Employee["`**Employee 员工**
---
• id: int
• name: str
• position: str
• salary: float
• hire_date: str
• is_active: bool`"]:::nestedModel
Project["`**Project 项目**
---
• id: int
• name: str
• description: str
• start_date: str
• end_date: Optional[str]
• budget: float
• status: str
---
**属性方法:**
• team_size()
• total_team_salary()`"]:::nestedModel
ContactInfo["`**ContactInfo 联系信息**
---
• email: str
• phone: str
• emergency_contact: str
• emergency_phone: str`"]:::nestedModel
Skill["`**Skill 技能**
---
• name: str
• level: int (1-10)
• years_experience: float`"]:::nestedModel
%% 枚举类型
PositionLevel["`**PositionLevel 职位等级**
---
• INTERN
• JUNIOR
• SENIOR
• LEAD
• MANAGER
• DIRECTOR`"]:::enumModel
%% 关系连接
Department -.->|"1:1 manager"| Employee
Department -.->|"1:N employees"| Employee
Department -.->|"1:N projects"| Project
Employee -.->|"1:1 level"| PositionLevel
Employee -.->|"1:1 contact"| ContactInfo
Employee -.->|"1:N skills"| Skill
Project -.->|"1:N team_members"| Employee
Project -.->|"1:1 project_manager"| Employee
%% 添加说明文本
subgraph Legend["图例说明"]
L1["`**主模型** - 系统核心业务实体`"]:::primaryModel
L2["`**嵌套模型** - 作为字段嵌入其他模型`"]:::nestedModel
L3["`**枚举模型** - 预定义选项类型`"]:::enumModel
L4["`**1:1** - 一对一关系`"]
L5["`**1:N** - 一对多关系`"]
end
%% 添加层次说明
subgraph Layers["嵌套层次"]
Layer1["`**第一层:** Department`"]
Layer2["`**第二层:** Employee, Project`"]
Layer3["`**第三层:** ContactInfo, Skill, PositionLevel`"]
Layer1 --> Layer2
Layer2 --> Layer3
end
from fastapi import FastAPI
from pydantic import BaseModel, Field
from typing import List, Optional
from enum import Enum
app = FastAPI()
class PositionLevel(str, Enum):
INTERN = "intern"
JUNIOR = "junior"
SENIOR = "senior"
LEAD = "lead"
MANAGER = "manager"
DIRECTOR = "director"
# 员工技能模型
class Skill(BaseModel):
name: str
level: int = Field(..., ge=1, le=10, description="技能等级 1-10")
years_experience: float = Field(..., ge=0, description="相关经验年数")
# 员工联系信息模型
class ContactInfo(BaseModel):
email: str = Field(..., regex=r'^[\w\.-]+@[\w\.-]+\.\w+$')
phone: str
emergency_contact: str
emergency_phone: str
# 员工模型
class Employee(BaseModel):
id: int
name: str
position: str
level: PositionLevel
salary: float = Field(..., gt=0, description="月薪")
skills: List[Skill] # 嵌套技能列表
contact: ContactInfo # 嵌套联系信息
hire_date: str
is_active: bool = True
# 项目模型
class Project(BaseModel):
id: int
name: str
description: str
start_date: str
end_date: Optional[str] = None
budget: float
team_members: List[Employee] # 嵌套员工列表
project_manager: Employee # 嵌套项目经理信息
status: str = "active"
@property
def team_size(self) -> int:
return len(self.team_members)
@property
def total_team_salary(self) -> float:
return sum(emp.salary for emp in self.team_members) + self.project_manager.salary
# 部门模型(包含多层嵌套)
class Department(BaseModel):
id: int
name: str
description: str
manager: Employee # 嵌套部门经理
employees: List[Employee] # 嵌套员工列表
projects: List[Project] # 嵌套项目列表
budget: float
@property
def total_employees(self) -> int:
return len(self.employees) + 1 # +1 for manager
@property
def department_salary_cost(self) -> float:
return sum(emp.salary for emp in self.employees) + self.manager.salary
@app.post("/departments/", response_model=Department)
async def create_department(department: Department):
print(f"部门 {department.name} 创建成功")
print(f"总员工数: {department.total_employees}")
print(f"薪资成本: {department.department_salary_cost}")
return department
@app.get("/departments/{dept_id}", response_model=Department)
async def get_department(dept_id: int):
# 模拟部门数据
sample_department = Department(
id=dept_id,
name="技术开发部",
description="负责公司核心产品的技术开发",
manager=Employee(
id=1,
name="李经理",
position="技术总监",
level=PositionLevel.DIRECTOR,
salary=25000.0,
skills=[
Skill(name="Python", level=9, years_experience=8.0),
Skill(name="项目管理", level=8, years_experience=6.0)
],
contact=ContactInfo(
email="li.manager@company.com",
phone="13800138000",
emergency_contact="李太太",
emergency_phone="13900139000"
),
hire_date="2018-03-15"
),
employees=[
Employee(
id=2,
name="王开发",
position="高级开发工程师",
level=PositionLevel.SENIOR,
salary=18000.0,
skills=[
Skill(name="Python", level=8, years_experience=5.0),
Skill(name="FastAPI", level=7, years_experience=2.0),
Skill(name="数据库设计", level=6, years_experience=4.0)
],
contact=ContactInfo(
email="wang.dev@company.com",
phone="13700137000",
emergency_contact="王妈妈",
emergency_phone="13600136000"
),
hire_date="2020-06-01"
)
],
projects=[
Project(
id=1,
name="客户管理系统升级",
description="升级现有CRM系统,增加AI功能",
start_date="2024-01-01",
budget=500000.0,
team_members=[
Employee(
id=3,
name="张前端",
position="前端工程师",
level=PositionLevel.JUNIOR,
salary=12000.0,
skills=[
Skill(name="Vue.js", level=7, years_experience=2.0),
Skill(name="TypeScript", level=6, years_experience=1.5)
],
contact=ContactInfo(
email="zhang.frontend@company.com",
phone="13500135000",
emergency_contact="张爸爸",
emergency_phone="13400134000"
),
hire_date="2022-09-01"
)
],
project_manager=Employee(
id=1, # 复用经理信息
name="李经理",
position="技术总监",
level=PositionLevel.DIRECTOR,
salary=25000.0,
skills=[
Skill(name="Python", level=9, years_experience=8.0),
Skill(name="项目管理", level=8, years_experience=6.0)
],
contact=ContactInfo(
email="li.manager@company.com",
phone="13800138000",
emergency_contact="李太太",
emergency_phone="13900139000"
),
hire_date="2018-03-15"
)
)
],
budget=2000000.0
)
return sample_department
@app.get("/departments/{dept_id}/projects/{project_id}", response_model=Project)
async def get_project_details(dept_id: int, project_id: int):
# 这个接口展示了如何返回深层嵌套的项目信息
dept = await get_department(dept_id)
project = next((p for p in dept.projects if p.id == project_id), None)
if project:
return project
else:
return {"error": "Project not found"}
if __name__ == "__main__":
import uvicorn
uvicorn.run(app, host="0.0.0.0", port=8001)
4.请求体更新数据
参数更新
在FastAPI中,请求体的参数更新是指通过HTTP方法(通常为PATCH或PUT)向后端发送部分或全部字段的数据,用以修改已有资源的部分属性,而非重写整个对象。这一机制要求模型的灵活性与兼容性,通常借助可选字段(Optional)或单独定义的更新模型来实现,使得客户端可以仅提交需要变更的字段,避免冗余传输和误更新。
重要特征:
- 特征1:支持部分字段更新:请求体允许只提供需变更的字段,而非完整资源。
- 特征2:字段应为可选:用于更新的Pydantic模型字段应为
Optional,否则会要求完整结构。 - 特征3:模型与业务逻辑解耦:更新模型与创建模型分离,便于维护和权限控制。
- 特征4:保持数据一致性与验证:更新后数据应重新验证或补全以确保资源完整性
更新用户个人简介
现象:
用户可以进入设置页面更新个人资料,如nickname或bio。该接口支持PATCH方法,仅提交变动字段。例如,只传入bio字段即可完成简介更新。
特征对比:
- ✔ 特征1(部分字段更新):用户可只提交一部分字段(如bio);
- ✔ 特征2(字段为Optional):更新模型中所有字段均为可选;
- ✔ 特征3(创建与更新模型分离):注册使用
UserCreate模型,更新使用UserUpdate模型; - ✔ 特征4(数据验证):后端根据提交字段进行字段验证并更新数据库中对应字段。
from fastapi import FastAPI
from pydantic import BaseModel
from typing import Optional
app = FastAPI()
# 正例1:社交平台用户更新接口
class UserUpdate(BaseModel):
nickname: Optional[str] = None
bio: Optional[str] = None
fake_user_db = {"nickname": "Alice", "bio": "Love coding"}
@app.patch("/user/profile", summary="更新用户资料")
def update_user(update: UserUpdate):
for key, value in update.dict(exclude_unset=True).items():
fake_user_db[key] = value
return fake_user_db
# 正例2:医院病历更新接口
class MedicalRecordUpdate(BaseModel):
diagnosis: Optional[str] = None
notes: Optional[str] = None
fake_medical_record = {"diagnosis": "Flu", "notes": "Prescribed rest"}
@app.patch("/medical/record", summary="更新病历内容")
def update_medical_record(update: MedicalRecordUpdate):
for key, value in update.dict(exclude_unset=True).items():
fake_medical_record[key] = value
return fake_medical_record
JSON转换与PUT更新
[[FastAPI:(7)路劲操作配置与JSON编码兼容]]
更新数据请用 HTTP PUT 操作。把输入数据转换为以 JSON 格式存储的数据(比如,使用 NoSQL 数据库时),可以使用 jsonable_encoder。例如,把 datetime 转换为 str。
from typing import List, Union
from fastapi import FastAPI
from fastapi.encoders import jsonable_encoder
from pydantic import BaseModel
app = FastAPI()
class Item(BaseModel):
name: Union[str, None] = None
description: Union[str, None] = None
price: Union[float, None] = None
tax: float = 10.5
tags: List[str] = []
items = {
"foo": {"name": "Foo", "price": 50.2},
"bar": {"name": "Bar", "description": "The bartenders", "price": 62, "tax": 20.2},
"baz": {"name": "Baz", "description": None, "price": 50.2, "tax": 10.5, "tags": []},
}
@app.get("/items/{item_id}", response_model=Item)
async def read_item(item_id: str):
return items[item_id]
@app.put("/items/{item_id}", response_model=Item)
async def update_item(item_id: str, item: Item):
update_item_encoded = jsonable_encoder(item)
items[item_id] = update_item_encoded
return update_item_encoded
用 PUT 把数据项 bar 更新为以下内容时:
{
"name": "Barz",
"price": 3,
"description": None,
}
因为上述数据未包含已存储的属性 "tax": 20.2,新的输入模型会把 "tax": 10.5 作为默认值。因此,本次操作把 tax 的值「更新」为 10.5。
PATCH
HTTP PATCH 操作用于更新 部分 数据。即,只发送要更新的数据,其余数据保持不变。
PATCH没有PUT知名,也怎么不常用。很多人甚至只用PUT实现部分更新。FastAPI 对此没有任何限制,可以随意互换使用这两种操作。
使用 Pydantic 的 exclude_unset 参数¶。更新部分数据时,可以在 Pydantic 模型的 .dict() 中使用 exclude_unset 参数。
比如,item.dict(exclude_unset=True)。这段代码生成的 dict 只包含创建 item 模型时显式设置的数据,而不包括默认值。然后再用它生成一个只含已设置(在请求中所发送)数据,且省略了默认值的 dict。
接下来,用 .copy() 为已有模型创建调用 update 参数的副本,该参数为包含更新数据的 dict。例如,stored_item_model.copy(update=update_data):
from typing import List, Union
from fastapi import FastAPI
from fastapi.encoders import jsonable_encoder
from pydantic import BaseModel
app = FastAPI()
class Item(BaseModel):
name: Union[str, None] = None
description: Union[str, None] = None
price: Union[float, None] = None
tax: float = 10.5
tags: List[str] = []
items = {
"foo": {"name": "Foo", "price": 50.2},
"bar": {"name": "Bar", "description": "The bartenders", "price": 62, "tax": 20.2},
"baz": {"name": "Baz", "description": None, "price": 50.2, "tax": 10.5, "tags": []},
}
@app.get("/items/{item_id}", response_model=Item)
async def read_item(item_id: str):
return items[item_id]
@app.patch("/items/{item_id}", response_model=Item)
async def update_item(item_id: str, item: Item):
stored_item_data = items[item_id]
stored_item_model = Item(**stored_item_data)
update_data = item.dict(exclude_unset=True) # 部分更新
updated_item = stored_item_model.copy(update=update_data) #参数副本
items[item_id] = jsonable_encoder(updated_item)
return updated_item