【Python开发】FastAPI 04:响应模型

响应模型是指在接口调用之后,服务器返回给客户端的数据模型。这个数据模型可以是一个简单的字符串,也可以是一个复杂的数据结构,如 JSON XML 格式的数据。本篇文章将详细介绍 FastAPI 中的响应模型。

目录

1 响应模型

1.1 response_model

1.2 response_model_exclude_unset

1.3 response_model_include 和 response_model_exclude

2 额外的模型

2.1 **user_in.dict()

① Pydantic 的 .dict()

② 解包

③ 额外关键字

2.2 减少重复代码

2.3 响应状态码

① demo

② HTTP 状态码

③ 名称捷径


📌源码地址:

https://gitee.com/yinyuu/fast-api_study_yinyu

1 响应模型

1.1 response_model

你可以在任意的路径操作中使用 response_model 参数来声明用于响应的模型:

  • @app.get()
  • @app.post()
  • @app.put()
  • @app.delete()
  • 等等。
from typing import Any, List, Union
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] = [] #默认值

@app.post("/items/", response_model=Item)
async def create_item(item: Item):
    return item

@app.get("/items/", response_model=List[Item])
async def read_items():
    return [
        {"name": "Portal Gun", "price": 42.0},
        {"name": "Plumbus", "price": 32.0},
    ]

注意:response_model 是「装饰器」方法(getpost 等)的一个参数。不像之前的所有参数和请求体,它不属于路径操作函数。

它接收的类型与你将为 Pydantic 模型属性所声明的类型相同,因此它可以是一个 Pydantic 模型,但也可以是一个由 Pydantic 模型组成的 list,例如 List[Item]

FastAPI 将使用此 response_model 来做以下事:

  • 将输出数据转换为其声明的类型;
  • 校验数据;
  • 在自动生成文档系统中使用;
  • 会将输出数据限制在该模型定义内!

📌 文档

1.2 response_model_exclude_unset

可以设置路径操作装饰器中的 response_model_exclude_unset=True 参数,然后响应中将不会包含那些默认值,而是仅有实际设置的值:

from typing import List, Union

from fastapi import FastAPI
from pydantic import BaseModel

app = FastAPI()

class Item1(BaseModel):
    name: str
    description: Union[str, None] = 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("/items3/{item_id}", response_model=Item1, response_model_exclude_unset=True)
async def read_item(item_id: str):
    return items[item_id]

若此时向路径操作发送 ID 为 foo 的商品的请求,则响应(不包括默认值)将为:

{
    "name": "Foo",
    "price": 50.2
}

📌 默认值字段有实际值的数据

若此时向路径操作发送 ID 为 bar 的商品的请求,则响应为:

{
    "name": "Bar",
    "description": "The bartenders",
    "price": 62,
    "tax": 20.2
}

这些值将包含在响应中。

📌 具有与默认值相同值的数据

如果数据具有与默认值相同的值,例如 ID 为 baz 的项:

{
    "name": "Baz",
    "description": None,
    "price": 50.2,
    "tax": 10.5,
    "tags": []
}

即使 descriptiontax 和 tags 具有与默认值相同的值,FastAPI 也能识别到这一点,它们的值被显式地所设定(而不是取自默认值),因此这些与默认值相同的值将包含在 JSON 响应中。

📌 你还可以使用:

  • response_model_exclude_defaults=True:等于其默认值的字段(无论是否设置)是否应从返回的字典中排除
  • response_model_exclude_none=True:是否应从返回的字典中排除等于 none 的字段

1.3 response_model_include 和 response_model_exclude

它们接收一个由属性名称 str 组成的 set 来包含(忽略其他的)或者排除(包含其他的)这些属性。

比如,如果要求返回的属性只有 name,那么可以使用 response_model_include,如果要求返回的属性排除掉 name,那么可以使用 response_model_exclude

...

@app.get(
    "/items/{item_id}/name",
    response_model=Item1,
    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=Item1, response_model_exclude={"tax"})
async def read_item_public_data(item_id: str):
    return items[item_id]

注意:{"name", "description"} 语法创建一个具有这两个值的 set。等同于 set(["name", "description"])

2 额外的模型

2.1 **user_in.dict()

① Pydantic 的 .dict()

Pydantic 模型具有 .dict() 方法,该方法返回一个拥有模型数据的 dict

首先我们像下面这样创建一个 Pydantic 对象 user_in

from typing import Union
from pydantic import BaseModel, EmailStr

class UserIn(BaseModel):
    username: str
    password: str
    email: EmailStr
    full_name: Union[str, None] = None

class UserInDB(BaseModel):
    username: str
    hashed_password: str  = None
    email: EmailStr
    full_name: Union[str, None] = None

user_in = UserIn(username="yinyu", password="secret", email="yinyu.doe@example.com")

然后调用并打印:

user_dict = user_in.dict()
print(user_dict)

我们将获得一个这样的 Python dict

{
    'username': 'yinyu',
    'password': 'secret',
    'email': 'yinyu.doe@example.com',
    'full_name': None,
}

② 解包

如果我们将 user_dict 这样的 dict 以 **user_dict 形式传递给一个函数(或类),Python将对其进行「解包」,它会将 user_dict 的键和值作为关键字参数直接传递。

从上面的 user_dict 继续编写:

user_db = UserInDB(**user_dict)

会产生类似于以下的结果:

UserInDB(
    username="yinyu",
    hashed_password=None,
    email="yinyu.doe@example.com",
    full_name=None,
)

需要注意的是,UserIn 和 UserInDB 可不是同一个模型,password 字段也不等同于 hashed_password 字段,也就是说在字段名一致的前提下,对应字段才会进行解包。

或者可以确切地进行如下表示:

UserInDB(
    username = user_dict["username"],
    hashed_password = user_dict["hashed_password"], #user_dic中没有该字段,那么会取默认值
    email = user_dict["email"],
    full_name = user_dict["full_name"]
)

③ 额外关键字

如上所述,hashed_password 字段没有取到值,这是因为字段名的不一致。

那么如何给这个 hashed_password 额外关键字设值呢,接上边代码如下:

user_dbb = UserInDB(**user_dict,hashed_password = "secret11")

最终结果如下:

UserInDB(
    username="yinyu",
    hashed_password="secret11",
    email="yinyu.doe@example.com",
    full_name=None,
)

2.2 减少重复代码

这是 FastAPI 的核心思想之一,上边的模型共享了大量数据,并拥有重复的属性名称和类型。

那么我们可以声明一个 UserBase 模型作为其他模型的基类。然后我们可以创建继承该模型属性(类型声明,校验等)的子类。

class UserBase(BaseModel):
    username: str
    email: EmailStr
    full_name: Union[str, None] = None

class UserIn1(UserBase):
    password: str

class UserInDB1(UserBase):
    hashed_password: str

2.3 响应状态码

① demo

与指定响应模型的方式相同,你也可以在以下任意的路径操作中使用 status_code 参数来声明用于响应的 HTTP 状态码:

  • @app.get()
  • @app.post()
  • @app.put()
  • @app.delete()
  • 等等。
from fastapi import FastAPI

app = FastAPI()

@app.post("/items1/", status_code=201)
async def create_item(name: str):
    return {"name": name}

status_code 参数接收一个表示 HTTP 状态码的数字。

📌 文档

② HTTP 状态码

在 HTTP 协议中,你将发送 3 位数的数字状态码作为响应的一部分。

简而言之:

  • 100 及以上状态码用于「消息」响应。很少直接使用,具有这些状态代码的响应不能带有响应体。
  • 200 及以上状态码用于「成功」响应,最常用。
  • 200 是默认状态代码,它表示一切「正常」
    • 另一个例子会是 201「已创建」。它通常在数据库中创建了一条新记录后使用。
    • 一个特殊的例子是 204「无内容」。此响应在没有内容返回给客户端时使用,因此该响应不能包含响应体。
  • 300 及以上状态码用于「重定向」。具有这些状态码的响应可能有或者可能没有响应体,但 304「未修改」是个例外,该响应不得含有响应体。
  • 400 及以上状态码用于「客户端错误」响应。这些可能是你第二常使用的类型。
    • 一个例子是 404,用于「未找到」响应。
    • 对于来自客户端的一般错误,你可以只使用 400
  • 500 及以上状态码用于服务器端错误。当你的应用程序代码或服务器中的某些部分出现问题时,它将自动返回这些状态代码之一。

③ 名称捷径

你不必去记住每个代码的含义。你可以使用来自 fastapi.status 的便捷变量。

from fastapi import FastAPI, status

app = FastAPI()

@app.post("/items2/", status_code=status.HTTP_201_CREATED)
async def create_item(name: str):
    return {"name": name}

这样使用你就可以使用编辑器的自动补全功能来查找它们:

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

尹煜

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值