apimodel 可以重复吗_fastapi教程翻译(十四): Extra Model(多个模型)

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

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

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

三、Union 和 anyOf

您可以将响应声明为两种类型的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}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值