Response Model
可以在路径操作中通过 response_model
参数声明用来返回的模型,例如
from typing import List
from fastapi import FastAPI
from pydantic import BaseModel
app = FastAPI()
class Item(BaseModel):
name: str
description: str = None
price: float
tax: float = None
tags: List[str] = []
@app.post("/items/", response_model=Item)
async def create_item(item: Item):
return item
注:response_model
是装饰器方法 get
,post
的参数,而不是 Query
等路径操作函数的参数
该参数的接受值具有和声明的 pydantic
模型相同属性,它可以是 Pydantic
模型,或者是 Pydantic
模型列表,如 List[Item]
FastAPI 将会使用 response_model
来:
- 将输出数据转化为其类型
- 验证数据
- 为返回数据添加 JSON 模式,用来 OpanAPI 路径操作中
- 用于自动文档生成系统
- 最重要的一点:对输出数据进行过滤
技术细节: response_model
作为 get
等而不是 Query
等的参数的原因是路径函数更可能会先返回 dict
,数据库对象或者其他的模型,再交由 response_model
来执行字段的限制和序列化
Return the same input data
声明一个 UserIn
模型,它包含一个明文密码字段
from fastapi import FastAPI
from pydantic import BaseModel, EmailStr
app = FastAPI()
class UserIn(BaseModel):
username: str
password: str
email: EmailStr
full_name: str = None
# Don't do this in production!
@app.post("/user/", response_model=UserIn)
async def create_user(*, user: UserIn):
return user
我们用同样的模型 UserIn
声明我们的输入和输出
当浏览器船舰一个带有密码的用户时,该接口会返回相同的密码
在上面的例子中,可能不是一个问题,因为是用户自己发送密码
但是如果我们再其他的路径操作中使用相同的模型,我们可能会把用户的密码发送给所有的客户端
危险:永远不要返回用户的明文密码
Add an output model
我们可以为输入和输出创建不同的模型,输出模型不包括密码
from fastapi import FastAPI
from pydantic import BaseModel, EmailStr
app = FastAPI()
class UserIn(BaseModel):
username: str
password: str
email: EmailStr
full_name: str = None
class UserOut(BaseModel):
username: str
email: EmailStr
full_name: str = None
@app.post("/user/", response_model=UserOut)
async def create_user(*, user: UserIn):
return user
在这个例子中,即使我们的路径操作函数接受包含密码的用户模型 UserIn
,我们设置返回模型 response_model
为 UserOut
,这将不包含用户的密码
FastAPI 将会借助 Pydantic
过滤掉所有不在输出模型中的数据
See it in the docs
当再自动文档中查看时,可以发现输入模型和输出模型具有它们独自的 JSON 模式,并且两个模型都会用于 api 交互文档
Response Model encoding parameters
返回的模型可以有默认值,即使 input model
不包含这些默认属性,在 response 中也会默认会包含他们
from typing import List
from fastapi import FastAPI
from pydantic import BaseModel
app = FastAPI()
class Item(BaseModel):
name: str
description: str = None
price: float
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, response_model_exclude_unset=True)
async def read_item(item_id: str):
return items[item_id]
description: str = None
有默认值None
tax: float = 10.5
有默认值10.5
tag: List[str] = []
有默认值[]
如果想要在他们没有实际值(只有默认值)的时候忽略他们,例如在 NoSQL 中模型有很多可选的属性,但是并不想返回所有具有默认值的 JSON response,可以设置 response_model_exclude_unset
为 True
from typing import List
from fastapi import FastAPI
from pydantic import BaseModel
app = FastAPI()
class Item(BaseModel):
name: str
description: str = None
price: float
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, response_model_exclude_unset=True)
async def read_item(item_id: str):
return items[item_id]
这样默认值将不会包含在返回值中,只有当数据实际存在时才会返回
如果发送 foo
,则返回的数据为
{
"name": "Foo",
"price": 50.2
}
注: FastAPI 使用 Pydantic
模型的 .dict()
做到这一点
但是如果数据在默认字段中有值,例如发送 bar
,将会返回其自身值,即
{
"name": "Bar",
"description": "The bartenders",
"price": 62,
"tax": 20.2
}
如果数据和默认字段的值相同,Pydantic 足够聪明去识别
注:默认值可以是任何数,而不只是 None
response_model_include
and response_model_exclude
装饰器参数 response_model_include
和 response_model_exclude
接受一个 str
的 set
,其中 str
是包含(忽略剩余)或者不包含(包含剩余)的属性名
这可以被用在只有一个 Pydantic
模型,并且试图快速从输出移出某个数据
注:这样的作法并不被推荐。因为 JSON 协议生成的 OpanAPI 仍然会包含整个模型,即使使用 response_model_include
或者 response_model_exclude
忽略了一些属性
from fastapi import FastAPI
from pydantic import BaseModel
app = FastAPI()
class Item(BaseModel):
name: str
description: str = None
price: float
tax: float = 10.5
items = {
"foo": {"name": "Foo", "price": 50.2},
"bar": {"name": "Bar", "description": "The Bar fighters", "price": 62, "tax": 20.2},
"baz": {
"name": "Baz",
"description": "There goes my baz",
"price": 50.2,
"tax": 10.5,
},
}
@app.get(
"/items/{item_id}/name",
response_model=Item,
response_model_include={"name", "description"},
)
async def read_item_name(item_id: str):
return items[item_id]
@app.get("/items/{item_id}/public", response_model=Item, response_model_exclude={"tax"})
async def read_item_public_data(item_id: str):
return items[item_id]
Using list
s instead of set
s
如果在 response_model_include
和 response_model_exclude
中使用 list
或者 tuple
,则 FastAPI 将会将他们自动转化为 set
并且正确工作
from fastapi import FastAPI
from pydantic import BaseModel
app = FastAPI()
class Item(BaseModel):
name: str
description: str = None
price: float
tax: float = 10.5
items = {
"foo": {"name": "Foo", "price": 50.2},
"bar": {"name": "Bar", "description": "The Bar fighters", "price": 62, "tax": 20.2},
"baz": {
"name": "Baz",
"description": "There goes my baz",
"price": 50.2,
"tax": 10.5,
},
}
@app.get(
"/items/{item_id}/name",
response_model=Item,
response_model_include=["name", "description"],
)
async def read_item_name(item_id: str):
return items[item_id]
@app.get("/items/{item_id}/public", response_model=Item, response_model_exclude=["tax"])
async def read_item_public_data(item_id: str):
return items[item_id]