跳到内容

响应模型 - 返回类型

您可以通过注解 路径操作函数返回类型 来声明用于响应的类型。

您可以像在函数 参数 中对输入数据一样使用 类型注解,您可以使用 Pydantic 模型、列表、字典、标量值(如整数、布尔值等)。

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
    tags: list[str] = []


@app.post("/items/")
async def create_item(item: Item) -> Item:
    return item


@app.get("/items/")
async def read_items() -> list[Item]:
    return [
        Item(name="Portal Gun", price=42.0),
        Item(name="Plumbus", price=32.0),
    ]
🤓 其他版本和变体
from typing import 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/")
async def create_item(item: Item) -> Item:
    return item


@app.get("/items/")
async def read_items() -> list[Item]:
    return [
        Item(name="Portal Gun", price=42.0),
        Item(name="Plumbus", price=32.0),
    ]
from typing import 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/")
async def create_item(item: Item) -> Item:
    return item


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

FastAPI 将使用此返回类型来

  • 验证 返回的数据。
    • 如果数据无效(例如,您缺少一个字段),这意味着 您的 应用程序代码已损坏,没有返回它应该返回的内容,它将返回服务器错误而不是返回不正确的数据。通过这种方式,您和您的客户端可以确信他们将收到预期的数据和数据形状。
  • 在 OpenAPI 路径操作 中添加响应的 JSON Schema
    • 这将用于 自动文档
    • 它也将用于自动客户端代码生成工具。

但最重要的是

  • 它将 限制和过滤 输出数据到返回类型中定义的内容。
    • 这对于 安全性 尤为重要,我们将在下面详细介绍。

response_model 参数

在某些情况下,您需要或希望返回一些与类型声明不完全相同的数据。

例如,您可能希望 返回一个字典 或数据库对象,但 将其声明为 Pydantic 模型。这样,Pydantic 模型将为返回的对象(例如字典或数据库对象)完成所有数据文档、验证等。

如果您添加了返回类型注解,工具和编辑器会抱怨(正确)错误,告诉您您的函数正在返回与您声明的类型(例如 Pydantic 模型)不同的类型(例如 dict)。

在这些情况下,您可以使用 路径操作装饰器 参数 response_model 而不是返回类型。

您可以在任何 路径操作 中使用 response_model 参数

  • @app.get()
  • @app.post()
  • @app.put()
  • @app.delete()
  • 等等。
from typing import Any

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
    tags: list[str] = []


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


@app.get("/items/", response_model=list[Item])
async def read_items() -> Any:
    return [
        {"name": "Portal Gun", "price": 42.0},
        {"name": "Plumbus", "price": 32.0},
    ]
🤓 其他版本和变体
from typing import Any, 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) -> Any:
    return item


@app.get("/items/", response_model=list[Item])
async def read_items() -> Any:
    return [
        {"name": "Portal Gun", "price": 42.0},
        {"name": "Plumbus", "price": 32.0},
    ]
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) -> Any:
    return item


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

注意

请注意,response_model 是“装饰器”方法(getpost 等)的一个参数。而不是您的 路径操作函数 的参数,就像所有其他参数和正文一样。

response_model 接收与您为 Pydantic 模型字段声明的类型相同的类型,因此,它可以是 Pydantic 模型,但也可以是,例如,Pydantic 模型的 list,如 List[Item]

FastAPI 将使用此 response_model 来完成所有数据文档、验证等,并将其 转换和过滤输出数据 为其类型声明。

提示

如果您的编辑器、mypy 等具有严格的类型检查,您可以将函数返回类型声明为 Any

这样,您就告诉编辑器您有意返回任何内容。但 FastAPI 仍将使用 response_model 进行数据文档、验证、过滤等。

response_model 优先级

如果您同时声明了返回类型和 response_model,则 response_model 将具有优先级并由 FastAPI 使用。

通过这种方式,即使您返回的类型与响应模型不同,您也可以为函数添加正确的类型注解,供编辑器和 mypy 等工具使用。而且您仍然可以让 FastAPI 使用 response_model 进行数据验证、文档等。

您还可以使用 response_model=None 来禁用为该 路径操作 创建响应模型,如果您要为不是有效 Pydantic 字段的内容添加类型注解,您可能需要这样做,您将在下面的一个部分中看到一个示例。

返回相同的输入数据

这里我们声明了一个 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 = None


# Don't do this in production!
@app.post("/user/")
async def create_user(user: UserIn) -> UserIn:
    return user
🤓 其他版本和变体
from typing import Union

from fastapi import FastAPI
from pydantic import BaseModel, EmailStr

app = FastAPI()


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


# Don't do this in production!
@app.post("/user/")
async def create_user(user: UserIn) -> UserIn:
    return user

信息

要使用 EmailStr,请首先安装 email-validator

确保你创建一个虚拟环境,激活它,然后安装它,例如:

$ pip install email-validator

或使用

$ pip install "pydantic[email]"

我们正在使用这个模型来声明我们的输入,并使用相同的模型来声明我们的输出

from fastapi import FastAPI
from pydantic import BaseModel, EmailStr

app = FastAPI()


class UserIn(BaseModel):
    username: str
    password: str
    email: EmailStr
    full_name: str | None = None


# Don't do this in production!
@app.post("/user/")
async def create_user(user: UserIn) -> UserIn:
    return user
🤓 其他版本和变体
from typing import Union

from fastapi import FastAPI
from pydantic import BaseModel, EmailStr

app = FastAPI()


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


# Don't do this in production!
@app.post("/user/")
async def create_user(user: UserIn) -> UserIn:
    return user

现在,每当浏览器创建带有密码的用户时,API 将在响应中返回相同的密码。

在这种情况下,这可能不是问题,因为是同一个用户发送密码。

但是,如果我们将相同的模型用于另一个 路径操作,我们可能会将用户的密码发送给每个客户端。

危险

除非您了解所有注意事项并知道自己在做什么,否则切勿存储用户的明文密码或以这种方式在响应中发送它。

添加输出模型

我们可以创建一个带有明文密码的输入模型和一个不带密码的输出模型

from typing import Any

from fastapi import FastAPI
from pydantic import BaseModel, EmailStr

app = FastAPI()


class UserIn(BaseModel):
    username: str
    password: str
    email: EmailStr
    full_name: str | None = None


class UserOut(BaseModel):
    username: str
    email: EmailStr
    full_name: str | None = None


@app.post("/user/", response_model=UserOut)
async def create_user(user: UserIn) -> Any:
    return user
🤓 其他版本和变体
from typing import Any, Union

from fastapi import FastAPI
from pydantic import BaseModel, EmailStr

app = FastAPI()


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


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


@app.post("/user/", response_model=UserOut)
async def create_user(user: UserIn) -> Any:
    return user

这里,尽管我们的 路径操作函数 正在返回包含密码的相同输入用户

from typing import Any

from fastapi import FastAPI
from pydantic import BaseModel, EmailStr

app = FastAPI()


class UserIn(BaseModel):
    username: str
    password: str
    email: EmailStr
    full_name: str | None = None


class UserOut(BaseModel):
    username: str
    email: EmailStr
    full_name: str | None = None


@app.post("/user/", response_model=UserOut)
async def create_user(user: UserIn) -> Any:
    return user
🤓 其他版本和变体
from typing import Any, Union

from fastapi import FastAPI
from pydantic import BaseModel, EmailStr

app = FastAPI()


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


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


@app.post("/user/", response_model=UserOut)
async def create_user(user: UserIn) -> Any:
    return user

...我们将 response_model 声明为我们的模型 UserOut,它不包含密码

from typing import Any

from fastapi import FastAPI
from pydantic import BaseModel, EmailStr

app = FastAPI()


class UserIn(BaseModel):
    username: str
    password: str
    email: EmailStr
    full_name: str | None = None


class UserOut(BaseModel):
    username: str
    email: EmailStr
    full_name: str | None = None


@app.post("/user/", response_model=UserOut)
async def create_user(user: UserIn) -> Any:
    return user
🤓 其他版本和变体
from typing import Any, Union

from fastapi import FastAPI
from pydantic import BaseModel, EmailStr

app = FastAPI()


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


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


@app.post("/user/", response_model=UserOut)
async def create_user(user: UserIn) -> Any:
    return user

因此,FastAPI 将负责过滤掉输出模型中未声明的所有数据(使用 Pydantic)。

response_model 或返回类型

在这种情况下,由于两个模型不同,如果我们将函数返回类型注解为 UserOut,编辑器和工具会抱怨我们正在返回无效类型,因为它们是不同的类。

这就是为什么在此示例中我们必须在 response_model 参数中声明它的原因。

...但请继续阅读下文,了解如何克服这个问题。

返回类型和数据过滤

让我们继续上一个示例。我们希望 使用一种类型注解函数,但我们希望能够从函数返回实际包含 更多数据 的内容。

我们希望 FastAPI 继续使用响应模型 过滤 数据。这样,即使函数返回更多数据,响应也只会包含响应模型中声明的字段。

在前面的示例中,由于类不同,我们必须使用 response_model 参数。但这也意味着我们无法获得编辑器和工具检查函数返回类型的支持。

但在大多数需要这样做的情况下,我们希望模型只是像此示例一样 过滤/移除 部分数据。

在这些情况下,我们可以使用类和继承来利用函数 类型注解,以在编辑器和工具中获得更好的支持,并且仍然获得 FastAPI 数据过滤

from fastapi import FastAPI
from pydantic import BaseModel, EmailStr

app = FastAPI()


class BaseUser(BaseModel):
    username: str
    email: EmailStr
    full_name: str | None = None


class UserIn(BaseUser):
    password: str


@app.post("/user/")
async def create_user(user: UserIn) -> BaseUser:
    return user
🤓 其他版本和变体
from typing import Union

from fastapi import FastAPI
from pydantic import BaseModel, EmailStr

app = FastAPI()


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


class UserIn(BaseUser):
    password: str


@app.post("/user/")
async def create_user(user: UserIn) -> BaseUser:
    return user

通过这种方式,我们获得了工具支持,来自编辑器和 mypy,因为这段代码在类型方面是正确的,但我们也获得了 FastAPI 的数据过滤。

这是如何工作的?让我们来看看。🤓

类型注解和工具

首先让我们看看编辑器、mypy 和其他工具会如何看待这个。

BaseUser 具有基本字段。然后 UserIn 继承自 BaseUser 并添加了 password 字段,因此,它将包含这两个模型的所有字段。

我们将函数返回类型注解为 BaseUser,但我们实际返回的是 UserIn 实例。

编辑器、mypy 和其他工具不会抱怨这一点,因为在类型术语中,UserInBaseUser 的子类,这意味着当预期是任何 BaseUser 时,它是一个 有效 类型。

FastAPI 数据过滤

现在,对于 FastAPI,它将查看返回类型并确保您返回的内容 包含类型中声明的字段。

FastAPI 在内部使用 Pydantic 执行几项操作,以确保不将相同的类继承规则用于返回的数据过滤,否则您最终可能会返回比预期更多的数据。

通过这种方式,您可以两全其美:带有 工具支持 的类型注解和 数据过滤

在文档中查看

当您查看自动文档时,您可以检查输入模型和输出模型都将拥有自己的 JSON Schema

并且两个模型都将用于交互式 API 文档

其他返回类型注解

在某些情况下,您返回的内容不是有效的 Pydantic 字段,并且您在函数中对其进行注解,只是为了获得工具(编辑器、mypy 等)提供的支持。

直接返回响应

最常见的情况是 直接返回响应,如高级文档中所述

from fastapi import FastAPI, Response
from fastapi.responses import JSONResponse, RedirectResponse

app = FastAPI()


@app.get("/portal")
async def get_portal(teleport: bool = False) -> Response:
    if teleport:
        return RedirectResponse(url="https://www.youtube.com/watch?v=dQw4w9WgXcQ")
    return JSONResponse(content={"message": "Here's your interdimensional portal."})

FastAPI 会自动处理这种简单情况,因为返回类型注解是 Response 类(或其子类)。

工具也会很高兴,因为 RedirectResponseJSONResponse 都是 Response 的子类,所以类型注解是正确的。

注解响应子类

您也可以在类型注解中使用 Response 的子类

from fastapi import FastAPI
from fastapi.responses import RedirectResponse

app = FastAPI()


@app.get("/teleport")
async def get_teleport() -> RedirectResponse:
    return RedirectResponse(url="https://www.youtube.com/watch?v=dQw4w9WgXcQ")

这也将起作用,因为 RedirectResponseResponse 的子类,FastAPI 将自动处理这种简单情况。

无效的返回类型注解

但是当您返回一些不是有效 Pydantic 类型的其他任意对象(例如数据库对象)并在函数中这样注解时,FastAPI 将尝试从该类型注解创建 Pydantic 响应模型,并将失败。

如果您在不同类型之间存在 联合,其中一个或多个不是有效的 Pydantic 类型,也会发生同样的情况,例如这会失败 💥

from fastapi import FastAPI, Response
from fastapi.responses import RedirectResponse

app = FastAPI()


@app.get("/portal")
async def get_portal(teleport: bool = False) -> Response | dict:
    if teleport:
        return RedirectResponse(url="https://www.youtube.com/watch?v=dQw4w9WgXcQ")
    return {"message": "Here's your interdimensional portal."}
🤓 其他版本和变体
from typing import Union

from fastapi import FastAPI, Response
from fastapi.responses import RedirectResponse

app = FastAPI()


@app.get("/portal")
async def get_portal(teleport: bool = False) -> Union[Response, dict]:
    if teleport:
        return RedirectResponse(url="https://www.youtube.com/watch?v=dQw4w9WgXcQ")
    return {"message": "Here's your interdimensional portal."}

...这会失败,因为类型注解不是 Pydantic 类型,也不是单个 Response 类或子类,它是 Responsedict 之间的联合(两者中的任何一个)。

禁用响应模型

继续上面的示例,您可能不希望拥有 FastAPI 执行的默认数据验证、文档、过滤等。

但您可能仍希望在函数中保留返回类型注解,以获得编辑器和类型检查器(例如 mypy)等工具的支持。

在这种情况下,您可以通过设置 response_model=None 来禁用响应模型生成

from fastapi import FastAPI, Response
from fastapi.responses import RedirectResponse

app = FastAPI()


@app.get("/portal", response_model=None)
async def get_portal(teleport: bool = False) -> Response | dict:
    if teleport:
        return RedirectResponse(url="https://www.youtube.com/watch?v=dQw4w9WgXcQ")
    return {"message": "Here's your interdimensional portal."}
🤓 其他版本和变体
from typing import Union

from fastapi import FastAPI, Response
from fastapi.responses import RedirectResponse

app = FastAPI()


@app.get("/portal", response_model=None)
async def get_portal(teleport: bool = False) -> Union[Response, dict]:
    if teleport:
        return RedirectResponse(url="https://www.youtube.com/watch?v=dQw4w9WgXcQ")
    return {"message": "Here's your interdimensional portal."}

这将使 FastAPI 跳过响应模型生成,这样您就可以拥有所需的任何返回类型注解,而不会影响您的 FastAPI 应用程序。🤓

响应模型编码参数

您的响应模型可能具有默认值,例如

from fastapi import FastAPI
from pydantic import BaseModel

app = FastAPI()


class Item(BaseModel):
    name: str
    description: 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("/items/{item_id}", response_model=Item, response_model_exclude_unset=True)
async def read_item(item_id: str):
    return items[item_id]
🤓 其他版本和变体
from typing import Union

from fastapi import FastAPI
from pydantic import BaseModel

app = FastAPI()


class Item(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("/items/{item_id}", response_model=Item, response_model_exclude_unset=True)
async def read_item(item_id: str):
    return items[item_id]
from typing import 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: 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: Union[str, None] = None(或 Python 3.10 中的 str | None = None)的默认值为 None
  • tax: float = 10.5 的默认值为 10.5
  • tags: List[str] = [] 的默认值为空列表:[]

但如果它们实际上并未存储,您可能希望将它们从结果中省略。

例如,如果您的 NoSQL 数据库中有许多可选属性的模型,但您不想发送充满默认值的非常长的 JSON 响应。

使用 response_model_exclude_unset 参数

您可以设置 路径操作装饰器 参数 response_model_exclude_unset=True

from fastapi import FastAPI
from pydantic import BaseModel

app = FastAPI()


class Item(BaseModel):
    name: str
    description: 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("/items/{item_id}", response_model=Item, response_model_exclude_unset=True)
async def read_item(item_id: str):
    return items[item_id]
🤓 其他版本和变体
from typing import Union

from fastapi import FastAPI
from pydantic import BaseModel

app = FastAPI()


class Item(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("/items/{item_id}", response_model=Item, response_model_exclude_unset=True)
async def read_item(item_id: str):
    return items[item_id]
from typing import 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: 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]

这些默认值将不会包含在响应中,只会包含实际设置的值。

因此,如果您向该 路径操作 发送 ID 为 foo 的项的请求,响应(不包括默认值)将是

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

信息

在 Pydantic v1 中,该方法名为 .dict(),在 Pydantic v2 中已弃用(但仍受支持),并更名为 .model_dump()

这里的示例使用 .dict() 以兼容 Pydantic v1,但如果您可以使用 Pydantic v2,则应改用 .model_dump()

信息

FastAPI 使用 Pydantic 模型的 .dict()exclude_unset 参数 来实现此目的。

信息

您也可以使用

  • response_model_exclude_defaults=True
  • response_model_exclude_none=True

Pydantic 文档 中所述,用于 exclude_defaultsexclude_none

带有默认值字段的数据

但是,如果您的数据具有模型字段的默认值,例如 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": []
}

FastAPI 足够聪明(实际上,Pydantic 足够聪明)能够识别出,即使 descriptiontaxtags 具有与默认值相同的值,它们也是明确设置的(而不是从默认值中获取)。

因此,它们将包含在 JSON 响应中。

提示

请注意,默认值可以是任何内容,而不仅仅是 None

它们可以是列表 ([])、float 类型(10.5)等。

response_model_includeresponse_model_exclude

您还可以使用 路径操作装饰器 参数 response_model_includeresponse_model_exclude

它们接受一个 str 类型的 set,其中包含要包含的属性名称(省略其余部分)或要排除的属性名称(包含其余部分)。

如果您只有一个 Pydantic 模型并且想从输出中删除一些数据,这可以作为一个快速的捷径。

提示

但仍然建议使用上述思路,使用多个类,而不是这些参数。

这是因为即使您使用 response_model_includeresponse_model_exclude 来省略某些属性,您的应用程序的 OpenAPI 中生成的 JSON Schema(和文档)仍将是完整模型的 JSON Schema。

这也适用于工作方式类似的 response_model_by_alias

from fastapi import FastAPI
from pydantic import BaseModel

app = FastAPI()


class Item(BaseModel):
    name: str
    description: str | None = 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]
🤓 其他版本和变体
from typing import Union

from fastapi import FastAPI
from pydantic import BaseModel

app = FastAPI()


class Item(BaseModel):
    name: str
    description: Union[str, None] = 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]

提示

语法 {"name", "description"} 创建一个包含这两个值的 set

它等同于 set(["name", "description"])

使用 list 而不是 set

如果您忘记使用 set 而使用了 listtuple,FastAPI 仍会将其转换为 set,并且会正常工作

from fastapi import FastAPI
from pydantic import BaseModel

app = FastAPI()


class Item(BaseModel):
    name: str
    description: str | None = 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]
🤓 其他版本和变体
from typing import Union

from fastapi import FastAPI
from pydantic import BaseModel

app = FastAPI()


class Item(BaseModel):
    name: str
    description: Union[str, None] = 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]

总结

使用 路径操作装饰器 的参数 response_model 来定义响应模型,尤其是为了确保私有数据被过滤掉。

使用 response_model_exclude_unset 仅返回显式设置的值。