跳到内容

响应模型 - 返回类型

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

你可以像用于函数**参数**中的输入数据一样使用**类型注解**,你可以使用 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 模型)不同。

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

信息

要使用 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 仅返回显式设置的值。