首页>文档>FastAPI>fastapi中文手册:Extra Model(多个模型)

fastapi中文手册:Extra Model(多个模型)

fastapi中文手册:Extra Model(多个模型)

FastAPI 是一个高性能 Web 框架,用于构建 API。

主要特性:

  • 快速:非常高的性能,与 NodeJS 和 Go 相当
  • 快速编码:将功能开发速度提高约 200% 至 300%
  • 更少的错误:减少约 40% 的人为错误
  • 直观:强大的编辑器支持,自动补全无处不在,调试时间更少
  • 简易:旨在易于使用和学习,减少阅读文档的时间。
  • 简短:减少代码重复。
  • 稳健:获取可用于生产环境的代码,具有自动交互式文档
  • 基于标准:基于并完全兼容 API 的开放标准 OpenAPI 和 JSON Schema 

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

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

  • input model 需要包含密码字段
  • output model 不能包含密码字段
  • database model可能包含一个被hash的密码值

危险⚠️

永远不要存储用户的明文密码,要使用一个安全的hash值去验证密码。

如果您不知道,您将了解什么是“密码哈希” 点击security chapters.

一、多个模型

下面写出了密码的使用地方和几个模型:

from fastapi import FastAPI
from pydantic import BaseModel
from pydantic.types import 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

1. 关于 **user_in.dict()

1.1. Pydantic的 .dict()方法

user_in 是一个 UserIn 类的Pydantic模型的实例.

Pydantic 模型有 .dict() 方法,该方法会将模型中的数据转化为 dict.

我们创建一个 Pydantic 对象 user_in,比如:

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

然后调用.dict()方法:

user_dict = user_in.dict()

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

查看一下user_dict:

print(user_dict)
# 输出
{
    'username': 'john',
    'password': 'secret',
    'email': 'john.doe@example.com',
    'full_name': None,
}
1.2. dict解包

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

因此, 使用上面的 user_dict :

UserInDB(**user_dict)

上面的**user_dict相当于如下的传参方式:

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

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

UserInDB(
    username = user_dict["username"],
    password = user_dict["password"],
    email = user_dict["email"],
    full_name = user_dict["full_name"],
)
1.3. 来自另一个内容的Pydantic模型

上面的例子中, 我们从 user_in.dict()得到了 user_dict ,如下:

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

等价于:

UserInDB(**user_in.dict())

因为 user_in.dict() 是一个 dict, 然后我们Python的 “解包**语法” 将参数传递到UserInDB

因此我们得到了一个Pydantic模型,但是数据是从另一个Pydantic模型.

1.4. 解包dict 和添加额外的keyword参数

添加额外关键字参数, hashed_password=hashed_password, 如下:

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

…等价于:

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

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

二、减少重复代码

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

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

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

我们可以做的更好.

我们可以定义UserBase模型,为我们其他的模型提供一个基类。然后我们其他的类可以继承这个UserBase模型,继承它的所有的属性。

这样我们只需要在每个子类中定义出其他的不同的属性即可:

from fastapi import FastAPI
from pydantic import BaseModel
from pydantic.types import 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

三、UnionanyOf

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

它会在 OpenAPI 中使用 anyOf 进行定义。

为此,请使用标准的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

同样的方式,你可以定义响应类型是模型组成的列表。

同样,你也需要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

四、用任意dict响应

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

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

这里你需要使用 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}
0 条回复 A文章作者 M管理员
    暂无讨论,说说你的看法吧
个人中心
购物车
优惠劵
今日签到
有新私信 私信列表
搜索