Skip to content

响应模型 - 返回类型

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

你可以像在函数 参数 中使用输入数据一样使用 类型注解,可以使用 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 将使用这个返回类型来:

  • 验证 返回的数据。
    • 如果数据无效(例如,你缺少某个字段),这意味着 你的 应用程序代码有问题,没有返回它应该返回的内容,并且它将返回服务器错误而不是返回不正确的数据。这样你和你的客户端可以确定他们会收到预期的数据和数据结构。
  • 为响应添加 JSON Schema,在 OpenAPI 路径操作 中。
    • 这将被用于 自动文档
    • 它也将被自动客户端代码生成工具使用。

但最重要的是:

  • 它将 限制和过滤 输出数据为返回类型中定义的内容。
    • 这对于 安全性 特别重要,我们将在下面看到更多相关内容。

response_model 参数

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

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

如果你添加了返回类型注解,工具和编辑器会用一个(正确的)错误提示你,你的函数返回的类型(例如字典)与你声明的类型(例如 Pydantic 模型)不同。

在这些情况下,你可以使用 路径操作装饰器 参数 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},
    ]

Note

注意 response_model 是 "装饰器" 方法(get, post 等)的参数。不是你的 路径操作函数 的参数,像所有参数和主体一样。

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

FastAPI 将使用这个 response_model 来进行所有数据文档化、验证等操作,并 转换和过滤输出数据 为其类型声明。

Tip

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

这样你告诉编辑器你是有意返回任何东西。但 FastAPI 仍将使用 response_model 进行数据文档化、验证、过滤等操作。

response_model 优先级

如果你同时声明了返回类型和 response_modelresponse_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

Info

要使用 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 将在响应中返回相同的密码。

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

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

Danger

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

添加输出模型

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

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 的项,响应(不包括默认值)将是:

{
    "名称": "Foo",
    "价格": 50.2
}

Info

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

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

Info

FastAPI 使用 Pydantic 模型的 .dict() 方法,并结合 exclude_unset 参数 来实现这一点。

Info

你也可以使用:

  • 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 响应中。

Tip

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

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

response_model_includeresponse_model_exclude

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

它们接受一个包含属性名称的 set,用于包含(省略其余部分)或排除(包含其余部分)。

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

Tip

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

这是因为即使你使用 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]

Tip

语法 {"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 来仅返回显式设置的值。