跳转至

响应模型 - 返回类型

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

你可以使用类型注解的方式,就像在函数参数中输入数据一样,你可以使用 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),
    ]
🤓 Other versions and variants
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 模型)不同。

在这些情况下,你可以使用路径操作装饰器参数 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},
    ]
🤓 Other versions and variants
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 是“装饰器”方法(getpost 等)的参数。而不是你的路径操作函数的参数,就像所有参数和请求体一样。

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

FastAPI 将使用此 response_model 执行所有数据文档、验证等操作,并转换和过滤输出数据以符合其类型声明。

Tip

如果你的编辑器、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
🤓 Other versions and variants
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
🤓 Other versions and variants
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
🤓 Other versions and variants
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
🤓 Other versions and variants
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
🤓 Other versions and variants
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
🤓 Other versions and variants
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."}
🤓 Other versions and variants
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."}
🤓 Other versions and variants
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]
🤓 Other versions and variants
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]
🤓 Other versions and variants
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
}

Info

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

这里的示例使用 .dict() 以与 Pydantic v1 兼容,但如果你可以使用 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

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

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

Tip

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

这是因为即使你使用 response_model_includeresponse_model_exclude 来省略某些属性,你的应用程序的 OpenAPI(和文档)中生成的 JSON Schema 仍然是完整模型的 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]
🤓 Other versions and variants
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]
🤓 Other versions and variants
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 仅返回显式设置的值。