在构建RESTful API时,经常需要处理复杂的数据关系。FastAPI通过支持多个关联模型,使得定义这些关系变得简单直观。这种方法不仅提高了代码的可维护性,还增强了API的灵活性。通过使用Pydantic库,我们可以轻松定义数据模型及其关联,从而在FastAPI应用中实现强大的数据处理逻辑。无论是一对多、多对一还是多对多的关系,FastAPI都能游刃有余。掌握这一技巧,将帮助你构建更加健壮和高效的API。

from fastapi import FastAPI, HTTPException
from pydantic import BaseModel, EmailStr
from typing import Optional

# 创建 FastAPI 应用实例
api = FastAPI()

# 定义输入用户模型
class NewUser(BaseModel):
    nickname: str  # 用户名
    secret: str  # 密码
    contact: EmailStr  # 邮箱
    real_name: Optional[str] = None  # 真实姓名,可选

# 定义输出用户模型
class ExistingUser(BaseModel):
    nickname: str  # 用户名
    contact: EmailStr  # 邮箱
    real_name: Optional[str] = None  # 真实姓名,可选

# 定义数据库用户模型
class UserDB(BaseModel):
    nickname: str  # 用户名
    secure_password: str  # 加密密码
    contact: EmailStr  # 邮箱
    real_name: Optional[str] = None  # 真实姓名,可选

# 模拟密码加密函数
def mock_password_encrypter(plain_text: str) -> str:  # 加密明文密码
    """加密明文密码"""
    return "encrypted_" + plain_text

# 模拟保存用户到数据库的函数
def mock_user_storage(new_user: NewUser) -> UserDB:  # 模拟保存用户到数据库
    """模拟将新用户保存到数据库"""
    secure_password = mock_password_encrypter(new_user.secret)
    user_db = UserDB(**new_user.dict(), secure_password=secure_password)
    """ **new_user.dict()双星号函数 """
    print("用户数据已存储!...虚拟的")
    return user_db

# 创建用户端点
@api.post("/add_user/", response_model=ExistingUser)
async def add_new_user(new_user: NewUser) -> ExistingUser:  # 创建新用户并返回用户信息
    """创建新用户并返回用户信息"""
    try:
        saved_user = mock_user_storage(new_user)
        return saved_user
    except Exception as e:
        raise HTTPException(status_code=500, detail=str(e))  # 服务器内部错误

注解说明:

  1. 字段注解:在 NewUser, ExistingUser, UserDB 类的字段定义中,添加了注释来描述每个字段的含义。

  2. 函数参数和返回值注解:在 mock_password_encryptermock_user_storage 函数定义中,添加了注释来描述函数的作用和返回值类型。

  3. 文档字符串:在每个函数和方法的开始处,添加了文档字符串(docstring),这些字符串详细描述了函数的作用和行为。

  4. 异常处理注解:在 add_new_user 函数中,添加了注释来描述异常处理逻辑。

  5. 双星号字典解包: **new_user.dict()

    • new_user.dict() 会将Pydantic模型的实例转换为一个普通的Python字典
    • 双星号是一个特殊的语法符号,用于表示双星号字典解包操作。当使用在函数调用中时,它允许你将一个字典对象的所有键值对作为关键字参数传递给函数。

这些注解和文档字符串有助于提高代码的可读性和可维护性,同时也使得代码更加易于理解

解包 dict 和更多关键字

例如:

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,
)