FastAPI 教程翻译 - 用户指南 15 - 响应模型

FastAPI 教程翻译 - 用户指南 15 - 响应模型

FastAPI Tutorial - User Guide - Response Model

You can declare the model used for the response with the parameter response_model in any of the path operations:

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

  • @app.get()

  • @app.post()

  • @app.put()

  • @app.delete()

  • etc.

    等等。

from typing import List

from fastapi import FastAPI
from pydantic import BaseModel

app = FastAPI()


class Item(BaseModel):
    name: str
    description: str = None
    price: float
    tax: float = None
    tags: List[str] = []


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

Note

注意

Notice that response_model is a parameter of the “decorator” method (get, post, etc). Not of your path operation function, like all the parameters and body.

请注意,response_model 是『装饰器』方法的参数(getpost 等)。像所有参数和主体一样,不是路径操作函数

It receives the same type you would declare for a Pydantic model attribute, so, it can be a Pydantic model, but it can also be, e.g. a list of Pydantic models, like List[Item].

它收到的类型与您为 Pydantic 模型属性声明的类型相同,因此它可以是 Pydantic 模型,但也可以是一个 Pydantic 模型的列表,像 List[Item]

FastAPI will use this response_model to:

FastAPI 将使用此 response_model 来:

  • Convert the output data to its type declaration.

    将输出数据转换为其类型声明。

  • Validate the data.

    验证数据。

  • Add a JSON Schema for the response, in the OpenAPI path operation.

    在 OpenAPI 的路径操作中为响应添加 JSON 模式。

  • Will be used by the automatic documentation systems.

    将由自动文档系统使用。

But most importantly:

但最重要的是:

  • Will limit the output data to that of the model. We’ll see how that’s important below.

    将输出数据限制为模型的数据。我们将在下面看到其重要性。

Technical Details

技术细节

The response model is declared in this parameter instead of as a function return type annotation, because the path function may not actually return that response model but rather return a dict, database object or some other model, and then use the response_model to perform the field limiting and serialization.

在此参数中声明响应模型,而不是将其声明为函数返回类型注释,因为路径函数实际上可能不会返回该响应模型,而是返回 dict、数据库对象或其他模型,然后使用 response_model 执行字段限制和序列化。

Return the same input data

返回相同的输入数据

Here we are declaring a UserIn model, it will contain a plaintext password:

在这里,我们声明一个 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


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

And we are using this model to declare our input and the same model to declare our output:

我们正在使用此模型声明输入,并使用同一模型声明输出:

from fastapi import FastAPI
from pydantic import BaseModel, EmailStr

app = FastAPI()


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


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

Now, whenever a browser is creating a user with a password, the API will return the same password in the response.

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

In this case, it might not be a problem, because the user himself is sending the password.

在这种情况下,这可能不是问题,因为用户自己正在发送密码。

But if we use the same model for another path operation, we could be sending our user’s passwords to every client.

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

Danger

危险

Never send the plain password of a user in a response.

切勿在响应中发送用户的明文密码。

Add an output model

添加输出模型

We can instead create an input model with the plaintext password and an output model without it:

我们可以改用纯文本密码创建输入模型,而没有明文密码的方式创建输出模型:

from fastapi import FastAPI
from pydantic import BaseModel, EmailStr

app = FastAPI()


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


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


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

Here, even though our path operation function is returning the same input user that contains the password:

在这里,即使我们的路径操作函数返回的是包含密码的相同的输入用户:

from fastapi import FastAPI
from pydantic import BaseModel, EmailStr

app = FastAPI()


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


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


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

…we declared the response_model to be our model UserOut, that doesn’t include the password:

…… 我们声明 response_model 为我们的模型 UserOut,其中不包含密码:

from fastapi import FastAPI
from pydantic import BaseModel, EmailStr

app = FastAPI()


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


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


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

So, FastAPI will take care of filtering out all the data that is not declared in the output model (using Pydantic).

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

See it in the docs

在文档中查看

When you see the automatic docs, you can check that the input model and output model will both have their own JSON Schema:

当您查看自动文档时,可以检查输入模型和输出模型是否都具有自己的 JSON 模式:

在这里插入图片描述

And both models will be used for the interactive API documentation:

两种模型都将用于交互式 API 文档:

Response Model encoding parameters

响应模型编码参数

Your response model could have default values, like:

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

from typing import List

from fastapi import FastAPI
from pydantic import BaseModel

app = FastAPI()


class Item(BaseModel):
    name: str
    description: str = 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: str = None has a default of None.

    description: str = None 的默认值为 None

  • tax: float = 10.5 has a default of 10.5.

    tax: float = 10.5 的默认值为 10.5

  • tags: List[str] = [] has a default of an empty list: [].

    tags: List[str] = [] 的默认值为空列表:[].

but you might want to omit them from the result if they were not actually stored.

但是如果它们实际上没有存储,则可能要从结果中忽略它们。

For example, if you have models with many optional attributes in a NoSQL database, but you don’t want to send very long JSON responses full of default values.

例如,如果您的模型在 NoSQL 数据库中具有很多可选属性,但您不想发送其中包含默认值的很长的 JSON 响应。

Use the response_model_exclude_unset parameter

使用 response_model_exclude_unset 参数

You can set the path operation decorator parameter response_model_exclude_unset=True:

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

from typing import List

from fastapi import FastAPI
from pydantic import BaseModel

app = FastAPI()


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

and those default values won’t be included in the response, only the values actually set.

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

So, if you send a request to that path operation for the item with ID foo, the response (not including default values) will be:

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

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

Info

信息

FastAPI uses Pydantic model’s .dict() with its exclude_unset parameter to achieve this.

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

Data with values for fields with defaults
具有默认值的字段的值的数据

But if your data has values for the model’s fields with default values, like the item with ID bar:

但是,如果您的数据中包含默认值的模型字段值,例如 ID 为 bar 的项目:

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

they will be included in the response.

它们将包含在响应中。

Data with the same values as the defaults
数据具有与默认值相同的值

If the data has the same values as the default ones, like the item with ID baz:

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

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

FastAPI is smart enough (actually, Pydantic is smart enough) to realize that, even though description, tax, and tags have the same values as the defaults, they were set explicitly (instead of taken from the defaults).

FastAPI 足够聪明(实际上是,Pydantic 足够聪明)可以意识到,即使 descriptiontaxtags 具有与默认值相同的值,它们也是显式设置的(而不是取自默认值)。

So, they will be included in the JSON response.

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

Tip

提示

Notice that the default values can be anything, not only None.

请注意,默认值可以是任何值,不仅可以是 None。

They can be a list ([]), a float of 10.5, etc.

它们可以是列表([])或者 10.5 这样的 float 等等。

response_model_include and response_model_exclude

response_model_includeresponse_model_exclude

You can also use the path operation decorator parameters response_model_include and response_model_exclude.

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

They take a set of str with the name of the attributes to include (omitting the rest) or to exclude (including the rest).

它们使用带有属性名称的 strset 集合来包括(省略其余部分)或排除(包括其余部分)。

This can be used as a quick shortcut if you have only one Pydantic model and want to remove some data from the output.

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

Tip

提示

But it is still recommended to use the ideas above, using multiple classes, instead of these parameters.

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

This is because the JSON Schema generated in your app’s OpenAPI (and the docs) will still be the one for the complete model, even if you use response_model_include or response_model_exclude to omit some attributes.

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

from fastapi import FastAPI
from pydantic import BaseModel

app = FastAPI()


class Item(BaseModel):
    name: str
    description: str = 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

提示

The syntax {"name", "description"} creates a set with those two values.

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

It is equivalent to set(["name", "description"]).

等效于 set(["name", "description"])

Using lists instead of sets
使用 list 代替 set

If you forget to use a set and use a list or tuple instead, FastAPI will still convert it to a set and it will work correctly:

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

from fastapi import FastAPI
from pydantic import BaseModel

app = FastAPI()


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

Recap

回顾

Use the path operation decorator’s parameter response_model to define response models and especially to ensure private data is filtered out.

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

Use response_model_exclude_unset to return only the values explicitly set.

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

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值