FastAPI 教程翻译 - 用户指南 16 - 额外模型

FastAPI 教程翻译 - 用户指南 16 - 额外模型

FastAPI Tutorial - User Guide - Extra Models

Continuing with the previous example, it will be common to have more than one related model.

继续前面的示例,通常会有多个相关模型。

This is especially the case for user models, because:

用户模型尤其如此,因为:

  • The input model needs to be able to have a password.

    输入模型必须具有密码。

  • The output model should not have a password.

    输出模型不应具有密码。

  • The database model would probably need to have a hashed password.

    数据库模型可能需要具有哈希密码。

Danger

危险

Never store user’s plaintext passwords. Always store a “secure hash” that you can then verify.

切勿存储用户的明文密码。始终存储『安全哈希』,然后可以进行验证。

If you don’t know, you will learn what a “password hash” is in the security chapters.

如果不知道,您将在 安全性章节 中了解『密码哈希』是什么。

Multiple models

多种模型

Here’s a general idea of how the models could look like with their password fields and the places where they are used:

这是关于模型的密码字段和使用位置的大致概念:

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


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


def fake_password_hasher(raw_password: str):
    return "supersecret" + raw_password


def fake_save_user(user_in: UserIn):
    hashed_password = fake_password_hasher(user_in.password)
    user_in_db = UserInDB(**user_in.dict(), hashed_password=hashed_password)
    print("User saved! ..not really")
    return user_in_db


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

About **user_in.dict()

关于 **user_in.dict()

Pydantic’s .dict()
Pydantic 的 .dict()

user_in is a Pydantic model of class UserIn.

user_in 是类 UserIn 的 Pydantic 模型。

Pydantic models have a .dict() method that returns a dict with the model’s data.

Pydantic 模型具有一个 .dict() 方法,该方法会返回一个带有模型数据的 dict

So, if we create a Pydantic object user_in like:

因此,如果我们创建一个 Pydantic 对象 user_in,例如:

user_in = UserIn(username="john", password="secret", email="john.doe@example.com")

and then we call:

然后我们调用:

user_dict = user_in.dict()

we now have a dict with the data in the variable user_dict (it’s a dict instead of a Pydantic model object).

现在,我们有一个 dict,其数据位于变量 user_dict 中(这是一个 dict,而不是 Pydantic 模型对象)。

And if we call:

如果我们调用:

print(user_dict)

we would get a Python dict with:

我们将得到一个 Python 的 dict

{
    'username': 'john',
    'password': 'secret',
    'email': 'john.doe@example.com',
    'full_name': None,
}
Unwrapping a dict
解包 dict

If we take a dict like user_dict and pass it to a function (or class) with **user_dict, Python will “unwrap” it. It will pass the keys and values of the user_dict directly as key-value arguments.

如果我们采用 user_dict 这样的 dict 并将其传递给带有 **user_dict 的函数(或类),Python 将对其进行『解包』。它将直接传递 user_dict 的键和值作为键值参数。

So, continuing with the user_dict from above, writing:

因此,继续上面的 user_dict,编写:

UserInDB(**user_dict)

Would result in something equivalent to:

会导致以下结果:

UserInDB(
    username="john",
    password="secret",
    email="john.doe@example.com",
    full_name=None,
)

Or more exactly, using user_dict directly, with whatever contents it might have in the future:

更确切地说,直接使用 user_dict,以及将来可能包含的任何内容:

UserInDB(
    username = user_dict["username"],
    password = user_dict["password"],
    email = user_dict["email"],
    full_name = user_dict["full_name"],
)
A Pydantic model from the contents of another
来自另一个内容的 Pydantic 模型

As in the example above we got user_dict from user_in.dict(), this code:

如上例所示,我们从 user_in.dict() 中获得了 user_dict,此代码:

user_dict = user_in.dict()
UserInDB(**user_dict)

would be equivalent to:

等同于:

UserInDB(**user_in.dict())

…because user_in.dict() is a dict, and then we make Python “unwrap” it by passing it to UserInDB prepended with **.

…… 因为 user_in.dict()dict,然后我们通过将 Python 传递给以 ** 开头的 UserInDB 来使其『解包』。

So, we get a Pydantic model from the data in another Pydantic model.

因此,我们从另一个 Pydantic 模型中的数据中获得了 Pydantic 模型。

Unwrapping a dict and extra keywords
解包 dict 和额外的关键字

And then adding the extra keyword argument hashed_password=hashed_password, like in:

然后添加额外的关键字参数 hashed_password=hashed_password,例如:

UserInDB(**user_in.dict(), hashed_password=hashed_password)

…ends up being like:

…… 最终就像:

UserInDB(
    username = user_dict["username"],
    password = user_dict["password"],
    email = user_dict["email"],
    full_name = user_dict["full_name"],
    hashed_password = hashed_password,
)

Warning

警告

The supporting additional functions are just to demo a possible flow of the data, but they of course are not providing any real security.

支持的附加功能仅仅是演示数据流的可能性,但是它们当然并不能提供任何真正的安全性。

Reduce duplication

减少重复

Reducing code duplication is one of the core ideas in FastAPI.

减少代码重复是 FastAPI 的核心思想之一。

As code duplication increments the chances of bugs, security issues, code desynchronization issues (when you update in one place but not in the others), etc.

随着代码重复的增加,错误、安全性问题,代码不同步问题(当您在一个地方进行更新而不是在另一个地方进行更新)等问题的机会也将增加。

And these models are all sharing a lot of the data and duplicating attribute names and types.

这些模型都共享大量数据,并复制属性名称和类型。

We could do better.

我们可以做得更好。

We can declare a UserBase model that serves as a base for our other models. And then we can make subclasses of that model that inherit its attributes (type declarations, validation, etc).

我们可以声明一个 UserBase 模型作为其他模型的基础。然后,我们可以使该模型的子类继承其属性(类型声明、验证等)。

All the data conversion, validation, documentation, etc. will still work as normally.

所有数据转换、验证、文档等仍将正常运行。

That way, we can declare just the differences between the models (with plaintext password, with hashed_password and without password):

这样,我们可以声明模型之间的差异(使用明文 password,使用 hashed_password 和不使用密码):

from fastapi import FastAPI
from pydantic import BaseModel, EmailStr

app = FastAPI()


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


class UserIn(UserBase):
    password: str


class UserOut(UserBase):
    pass


class UserInDB(UserBase):
    hashed_password: str


def fake_password_hasher(raw_password: str):
    return "supersecret" + raw_password


def fake_save_user(user_in: UserIn):
    hashed_password = fake_password_hasher(user_in.password)
    user_in_db = UserInDB(**user_in.dict(), hashed_password=hashed_password)
    print("User saved! ..not really")
    return user_in_db


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

Union or anyOf

UnionanyOf

You can declare a response to be the Union of two types, that means, that the response would be any of the two.

您可以将响应声明为两种类型的 Union,这意味着该响应将是两种类型中的任何一种。

It will be defined in OpenAPI with anyOf.

将在 OpenAPI 中使用 anyOf 进行定义。

To do that, use the standard Python type hint typing.Union:

为此,请使用标准的 Python 类型提示 typing.Union

from typing import Union

from fastapi import FastAPI
from pydantic import BaseModel

app = FastAPI()


class BaseItem(BaseModel):
    description: str
    type: str


class CarItem(BaseItem):
    type = "car"


class PlaneItem(BaseItem):
    type = "plane"
    size: int


items = {
    "item1": {"description": "All my friends drive a low rider", "type": "car"},
    "item2": {
        "description": "Music is my aeroplane, it's my aeroplane",
        "type": "plane",
        "size": 5,
    },
}


@app.get("/items/{item_id}", response_model=Union[PlaneItem, CarItem])
async def read_item(item_id: str):
    return items[item_id]

List of models

型号清单

The same way, you can declare responses of lists of objects.

同样,您可以声明对象列表的响应。

For that, use the standard Python typing.List:

为此,请使用标准的 Python typing.List

from typing import List

from fastapi import FastAPI
from pydantic import BaseModel

app = FastAPI()


class Item(BaseModel):
    name: str
    description: str


items = [
    {"name": "Foo", "description": "There comes my hero"},
    {"name": "Red", "description": "It's my aeroplane"},
]


@app.get("/items/", response_model=List[Item])
async def read_items():
    return items

Response with arbitrary dict

带有任意 dict 的响应

You can also declare a response using a plain arbitrary dict, declaring just the type of the keys and values, without using a Pydantic model.

您还可以使用简单的任意 dict 声明响应,仅声明键和值的类型,而无需使用 Pydantic 模型。

This is useful if you don’t know the valid field/attribute names (that would be needed for a Pydantic model) beforehand.

如果您事先不知道有效的字段/属性名称(Pydantic 模型需要此名称),这将很有用。

In this case, you can use typing.Dict:

在这种情况下,您可以使用 typing.Dict

from typing import Dict

from fastapi import FastAPI

app = FastAPI()


@app.get("/keyword-weights/", response_model=Dict[str, float])
async def read_keyword_weights():
    return {"foo": 2.3, "bar": 3.4}

Recap

回顾

Use multiple Pydantic models and inherit freely for each case.

使用多个 Pydantic 模型,并针对每种情况自由继承。

You don’t need to have a single data model per entity if that entity must be able to have different “states”. As the case with the user “entity” with a state including password, password_hash and no password.

如果每个实体必须能够具有不同的『状态』,则不需要每个实体具有单个数据模型。以用户『实体』为例,其状态包括 passwordpassword_hash 和没有密码。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
fastapi-mysql-generator 是一个用于快速生成FastAPI和MySQL的框架的工具。FastAPI是一个现代、快速(高性能)的web框架,而MySQL是一个流行的关系型数据库。 使用 fastapi-mysql-generator,可以从一个简单的命令行界面中生成 FastAPI 应用程序,并与 MySQL 数据库集成。这个工具可以自动创建数据库表和模型(Model),并提供一组 API 端点,用于执行常见的 CRUD(创建、读取、更新和删除)操作。 fastapi-mysql-generator 的主要优势之一是它的简单易用性。无论是初学者还是有经验的开发人员,都可以快速上手并生成一个完整的基于FastAPI和MySQL的应用程序。只需要几个简单的步骤,就可以生成项目的基本结构和代码。同时,fastapi-mysql-generator 还提供了一些配置选项,可以根据需求进行自定义设置,以满足特定的应用程序需求。 这个工具还提供了一些有用的特性,以增强开发的效率和便利性。例如,它可以自动生成 API 文档,包括请求和响应模型的文档说明。此外,fastapi-mysql-generator 还支持身份验证和授权功能,以确保 API 路由的安全性。 总而言之,fastapi-mysql-generator 是一个快速生成 FastAPI 和 MySQL 应用程序的方便工具。它简化了应用程序的开发过程,并提供了一些有用的特性,以提高开发效率和便利性。无论是初学者还是有经验的开发人员,都可以受益于这个工具的使用。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值