FastAPI Web框架 [1.8]
Posted Ch4536251
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了FastAPI Web框架 [1.8]相关的知识,希望对你有一定的参考价值。
额外的模型
# 额外的模型
# 拥有多个相关的模型是很常见的:
# 对用户模型来说尤其如此,因为:
# 输入模型需要拥有密码属性。
# 输出模型不应该包含密码。
# 数据库模型很可能需要保存密码的哈希值。
# !!!!永远不要存储用户的明文密码。始终存储一个可以用于验证的「安全哈希值」。
# 如果你尚未了解该知识,你可以在安全章节中学习何为「密码哈希值」。
# 多个模型
# 根据它们的密码字段以及使用位置去定义模型的大概思路:
from typing import Optional,Union,List,Dict
from fastapi import FastAPI
from pydantic import BaseModel,EmailStr
app=FastAPI()
class UserIn(BaseModel):
username:str
password:str
email:EmailStr
full_name:Optional[str] = None
class UserOut(BaseModel):
username:str
email:EmailStr
full_name:Optional[str] = None
class UserInDB(BaseModel):
username:str
hashed_password:str
email:EmailStr
full_name:Optional[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.d,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
# 关于 **user_in.dict()
# Pydantic 的 .dict(),,user_in 是一个 UserIn 类的 Pict()ydantic 模型; Pydantic 模型具有 .dict() 方法,该方法返回一个拥有模型数据的 dict。
# 因此,如果我们像下面这样创建一个 Pydantic 对象 user_in:
# user_in = UserIn(username="john", password="secret", email="john.doe@example.com")
# 然后我们调用:
# user_dict = user_in.dict()
# 现在我们有了一个数据位于变量 user_dict 中的 dict(它是一个 dict 而不是 Pydantic 模型对象)。
# 如果我们调用: print(user_dict)
# 我们将获得一个这样的 Python dict:
#
# 'username': 'john',
# 'password': 'secret',
# 'email': 'john.doe@example.com',
# 'full_name': None,
#
# 解包 dict
# 如果我们将 user_dict 这样的 dict 以 **user_dict 形式传递给一个函数(或类),Python将对其进行「解包」。它会将 user_dict 的键和值作为关键字参数直接传递。
# 从上面的 user_dict 继续,编写:UserInDB(**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"],
# )
#
# 上例所示,我们从 user_in.dict() 中获得了 user_dict,此代码:
#
# user_dict = user_in.dict()
# UserInDB(**user_dict)
# 等同于:UserInDB(**user_in.dict())
# 因为 user_in.dict() 是一个 dict,然后我们通过以**开头传递给 UserInDB 来使 Python「解包」它。
# 这样,我们获得了一个来自于其他 Pydantic 模型中的数据的 Pydantic 模型。
# 解包 dict 和额外关键字
# 然后添加额外的关键字参数 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,
# )
# Warning :辅助性的额外函数只是为了演示可能的数据流,但它们显然不能提供任何真正的安全性。
# 减少重复
# 减少代码重复是 FastAPI 的核心思想之一。
# 因为代码重复会增加出现 bug、安全性问题、代码失步问题(当你在一个位置更新了代码但没有在其他位置更新)等的可能性。
# 上面的这些模型都共享了大量数据,并拥有重复的属性名称和类型。
# 我们可以做得更好。
#
# 我们可以声明一个 UserBase 模型作为其他模型的基类。然后我们可以创建继承该模型属性(类型声明,校验等)的子类。
# 所有的数据转换、校验、文档生成等仍将正常运行。
# 这样,我们可以仅声明模型之间的差异部分(具有明文的 password、具有 hashed_password 以及不包括密码)。
class UserBase(BaseModel):
usename:str
email:EmailStr
full_name:Optional[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 进行定义。
# 定义一个 Union 类型时,首先包括最详细的类型,然后是不太详细的类型。在下面的示例中,更详细的 PlaneItem 位于 Union[PlaneItem,CarItem] 中的 CarItem 之前。
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]
# 模型列表
# 可以用同样的方式声明由对象列表构成的响应。
# 为此,请使用标准的 Python typing.List
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:
@app.get("/keyword-weights/",response_model=Dict[str,float])
async def read_keyword_weights():
return "foo":2.3,"bar":3.4
# 使用多个 Pydantic 模型,并针对不同场景自由地继承。
# 如果一个实体必须能够具有不同的「状态」 ,你无需为每个状态的实体定义单独的数据模型。以用户「实体」为例,其状态有包含 password、包含 password_hash 以及不含密码。
响应状态码
# 响应状态码
# 与指定响应模型的方式相同,你也可以在以下任意的路径操作中使用 status_code 参数来声明用于响应的 HTTP 状态码:
# @app.get()
# @app.post()
# @app.put()
# @app.delete()
from fastapi import FastAPI
app = FastAPI()
@app.post("/items/",status_code=201)
async def create_item(name:str):
return "name":name
# 注意,status_code 是「装饰器」方法(get,post 等)的一个参数。不像之前的所有参数和请求体,它不属于路径操作函数。
# status_code 参数接收一个表示 HTTP 状态码的数字; status_code 也能够接收一个 IntEnum 类型,比如 Python 的 http.HTTPStatus
# 关于 HTTP 状态码
# 在 HTTP 协议中,你将发送 3 位数的数字状态码作为响应的一部分。
#
# 这些状态码有一个识别它们的关联名称,但是重要的还是数字。
#
# 简而言之:
# 100 及以上状态码用于「消息」响应。你很少直接使用它们。具有这些状态代码的响应不能带有响应体。
# 200 及以上状态码用于「成功」响应。这些是你最常使用的。
# 200 是默认状态代码,它表示一切「正常」。
# 另一个例子会是 201,「已创建」。它通常在数据库中创建了一条新记录后使用。
# 一个特殊的例子是 204,「无内容」。此响应在没有内容返回给客户端时使用,因此该响应不能包含响应体。
# 300 及以上状态码用于「重定向」。具有这些状态码的响应可能有或者可能没有响应体,但 304「未修改」是个例外,该响应不得含有响应体。
# 400 及以上状态码用于「客户端错误」响应。这些可能是你第二常使用的类型。
# 一个例子是 404,用于「未找到」响应。
# 对于来自客户端的一般错误,你可以只使用 400。
# 500 及以上状态码用于服务器端错误。你几乎永远不会直接使用它们。当你的应用程序代码或服务器中的某些部分出现问题时,它将自动返回这些状态代码之一。
# 记住名称的捷径
from fastapi import FastAPI
app = FastAPI()
@app.post("/items/", status_code=201) # 201 是表示「已创建」的状态码。
async def create_item(name: str):
return "name": name
# 可以使用来自 fastapi.status 的便捷变量。
from fastapi import FastAPI, status
app = FastAPI()
@app.post("/items/", status_code=status.HTTP_201_CREATED)
async def create_item(name: str):
return "name": name
# 你也可以使用 from starlette import status。
# 为了给你(即开发者)提供方便,FastAPI 提供了与 starlette.status 完全相同的 fastapi.status。但它直接来自于 Starlette。
以上是关于FastAPI Web框架 [1.8]的主要内容,如果未能解决你的问题,请参考以下文章