FastAPI(21)- 多个模型的代码演进
前言
在一个完整的应用程序中,通常会有很多个相关模型,比如
- 请求模型需要有 password
- 响应模型不应该有 password
- 数据库模型可能需要一个 hash 加密过的 password
多个模型的栗子
需求
- 注册功能
- 请求输入密码
- 响应不需要输出密码
- 数据库存储加密后的密码
实际代码
#!usr/bin/env python # -*- coding:utf-8 _*- """ # author: 小菠萝测试笔记 # blog: https://www.cnblogs.com/poloyy/ # time: 2021/9/22 8:28 上午 # file: 19_extra models.py """ import uvicorn from fastapi import FastAPI from typing import Optional 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(password: str) -> str: return "supersecret" + password # 数据库存储 def fake_save_user(user: UserIn): # 取出用户的密码进行加密 hash_password = fake_password_hasher(user.password) # 转换为数据库模型 userInDB = UserInDB(**user.dict(), hashed_password=hash_password) # 返回数据 return userInDB @app.post("/user", response_model=UserOut) async def create_user(user: UserIn): # 创建用户,落库 user_saved = fake_save_user(user) # 返回存储后的用户信息 return user_saved if __name__ == "__main__": uvicorn.run(app="19_extra_models:app", host="127.0.0.1", port=8080, reload=True, debug=True)
.dict()
是 Pydantic 提供的方法,将模型的实例对象转换为 dict
**user.dict()
先将 user 转成 dict,然后解包
减少代码重复
核心思想
- 减少代码重复是 FastAPI 的核心思想之一。
- 因为代码重复增加了错误、安全问题、代码同步问题(当在一个地方更新而不是在其他地方更新时)等的可能性
上面代码存在的问题
三个模型都共享大量数据
利用 Python 继承的思想进行改造
- 声明一个 UserBase 模型,作为其他模型的基础
- 然后创建该模型的子类来继承其属性(类型声明、验证等),所有数据转换、验证、文档等仍然能正常使用
- 这样,不同模型之间的差异(使用明文密码、使用哈希密码、不使用密码)也很容易识别出来
#!usr/bin/env python # -*- coding:utf-8 _*- """ # author: 小菠萝测试笔记 # blog: https://www.cnblogs.com/poloyy/ # time: 2021/9/22 8:28 上午 # file: 19_extra models.py """ import uvicorn from fastapi import FastAPI from typing import Optional from pydantic import BaseModel, EmailStr app = FastAPI() # 基类模型 class UserBase(BaseModel): username: 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(password: str) -> str: return "supersecret" + password # 数据库存储 def fake_save_user(user: UserIn): # 取出用户的密码进行加密 hash_password = fake_password_hasher(user.password) # 转换为数据库模型 userInDB = UserInDB(**user.dict(), hashed_password=hash_password) # 返回数据 return userInDB @app.post("/user", response_model=UserOut) async def create_user(user: UserIn): # 创建用户,落库 user_saved = fake_save_user(user) # 返回存储后的用户信息 return user_saved if __name__ == "__main__": uvicorn.run(app="19_extra models:app", host="127.0.0.1", port=8080, reload=True, debug=True)