JSON兼容编码器
在某些情况下,可能需要将数据类型(如Pydantic模型)转换为与JSON兼容的数据类型(如dict、list等),对于这种要求,fastapi提供了jsonable_encoder()函数。
不兼容示例
from datetime import datetime
from typing import Union
from fastapi import FastAPI
from fastapi.encoders import jsonable_encoder
from pydantic import BaseModel
fake_db = {}
class Item(BaseModel):
title: str
timestamp: datetime
description: Union[str, None] = None
app = FastAPI()
# 演示不使用jsonable_encoder编码
@app.post("/items/{id}")
def insert_item(id: str, item: Item):
fake_db[id] = item
print(fake_db)
if __name__ == "__main__":
import uvicorn
uvicorn.run(app="main:app",host="127.0.0.1",port=8080,reload=True)
当不进行转码时fake_db中存储的内容类似下图:
一个类带属性的,默认输出格式。
兼容示例
# 演示使用jsonable_encoder编码
@app.put("/items/{id}")
def update_item(id: str, item: Item):
json_compatible_item_data = jsonable_encoder(item)
fake_db[id] = json_compatible_item_data
print(fake_db)
这个操作不会返回一个包含JSON格式(作为字符串)数据的庞大的str。它将返回一个Python标准数据结构(例如dict),其值和子值都与JSON兼容。
请求体更新数据
fastapi中通常使用put或patch两种方法来更新数据。两者可以进行互换,通常更新全部数据用PUT,更新部分数据用PATCH。
引出更新数据时应注意的问题
当数据模型中存在默认值时,如果直接利用这个模型更新数据,会产生意想不到的结果。
后端代码如下:
# 演示更新数据时默认值产生的后果
class Student(BaseModel):
name:Union[str,None] = None
description:Union[str,None] = None
sex:Union[str,None]=None
age:int = 18
hobby:List[str] = []
# 模拟目前存在的一些学生信息
students = [
{"name":"jack","age":20},
{"name":"tom","description":"he is a boy","sex":"male"},
{"name":"jerry","hobby":["basketball","games"]}
]
# 定义路径装饰器和路径操作函数来更新某个student信息
@app.patch("/students/{id}")
async def update_student(id:int,stu:Student):
if id<len(students):
students[id]=jsonable_encoder(stu)
else:
raise HTTPException(status_code=404,detail="out of range")
return students
测试结果:
从结果看出当更新第三个student的年龄时,由于只输入了age字段,其他字段默认为None或[]。在默认情况下更新时,默认值会参与更新,导致原来jerry同学的name,hobby字段的信息丢失。
解决更新时默认值问题
主要是利用BaseModel子类的copy函数的update参数和dict函数中的exclude_unset参数,来解决这个问题。
后端代码如下:
# 演示通过BaseModel子类的dict函数的exclude_unset参数解决此类问题
@app.put("/students/{id}")
async def update_student(id:int,stu:Student):
if id<len(students):
# 第一步:取出原存储的数据
stu_stored = students[id]
# 第二步:根据原存储数据创建原数据的数据模型对象
stu_model = Student(**stu_stored)
# 第三步:利用BaseModel子类的dict函数的exclude_unset参数取出改变的字段字典
update_data = stu.dict(exclude_unset=True)
# 第四步:利用BaseModel子类的copy函数的update参数更新指定字段数据
update_item = stu_model.copy(update=update_data)
# 第五步:用更新后的数据对象替换原数据对象,完成更换
students[id]=jsonable_encoder(update_item)
else:
raise HTTPException(status_code=404,detail="out of range")
return students