Response Model


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 是装饰器方法 getpost 的参数,而不是 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_modelUserOut,这将不包含用户的密码

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_unsetTrue

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_includeresponse_model_exclude 接受一个 strset,其中 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 lists instead of sets

如果在 response_model_includeresponse_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]

文章作者: qiufeng
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 qiufeng !
评论
 上一篇
Extra Models Extra Models
Extra Models有时候我们常常会有不止一个关系模型 用户模型尤其如此,因为: input model 需要有密码字段 output model 不应该有密码字段 database model 需要有哈希密码字段 警告:永远不要存
2020-04-16
下一篇 
Header Parameters Header Parameters
Header Parameters定义 Header 参数也和定义 Query 等相同 例子from fastapi import FastAPI, Header app = FastAPI() @app.get("/items/")
2020-04-14
  目录