ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

FastAPI 难点解惑 | Python语言的后端框架

FastAPI 难点解惑 | Python语言的后端框架

中秋前就决定要开FastAPI的学习了,还是一直拖到国庆前才开始。还好作者之前有过SpringBoot框架及其相关知识的学习和实践,花了大概两周时间一般看知识点一遍跟着敲,中间有疑惑的地方就给智能体帮我解惑,也是顺利的学习完了FastAPI这个后端框架。整体上还是和SpringBoot有相似之处,但是其中某些功能的实现还是有区别,看视频是自己也总结了一些难点或者是一些我觉得算是“优雅”的实现,本篇文章就围绕这些点来谈一谈。

首先第一个难点就是依赖注入,这是一个将通用逻辑或者代码用于路由函数或者别的函数的参数传入,并可以自动执行封装好的代码体逻辑的一个实现。下面就直接拿后端项目中的路由函数来演示其作用以及原理:

@router.get("/info") async def get_user_info(user: User = Depends(get_current_user)): # 获取用户信息逻辑:验证用户是否存在 -> 获取用户信息 -> 响应结果: 依赖注入 return success_response(message="获取用户信息成功",data=UserInfoResponse.model_validate(user))

这是一个获取用户信息的路由函数,可以看到传入的参数是一个类型为User的对象并有默认值,默认值就是由依赖注入实现,注意这里使用依赖注入是调用了Depends这个方法,传入的值是要注入的函数名,而不是对函数的调用(如get_current_user(),添加了括号后表示对函数的调用)。下面就是get_current_user函数的方法体:

async def get_current_user(authorization: str = Header(...,alias="Authorization"), db:AsyncSession=Depends(get_db)): # Bearer xxxxx # token=authorization.split(" ")[1] token = authorization.replace("Bearer ","") user = await get_user_by_token(db, token) if not user: raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED,detail="无效的令牌或令牌过期") return user

由于篇幅原因,原谅作者详细的内容就不在这里说了,大概就是从前端返回的请求头中提取token进行对用户账号的鉴权。学过后端的同学都知道,鉴权在大量的接口会用到,是需要高频被调用的逻辑片段,依赖注入就是通过fastapi框架来对注入的函数进行调用来实现对鉴权的便捷使用,调用的时刻就是在执行当前函数的方法体之前,而且可以嵌套依赖注入,会深度执行。上面的代码就是在被注入的get_current_user参数中也有db:AsyncSession=Depends(get_db)这一层依赖注入。嵌套的注入就可以实现逻辑重复调用的嵌套,作者认为是很优雅的一种方式呢~

在路由中使用ORM也是一个难点,但我觉得理解了之后才能更好的在后面去运用它。ORM是一种编程技术,通过使用操作对象的方式而不是使用sql语句来操作数据库。在上面的代码已经出现了db:AsyncSession=Depends(get_db)这样的对象类型,AsyncSession对象其实就是数据库的会话对象,这个对象怎么生成的呢,我们先看下面的代码:

ASYNC_DATABASE_URL = "mysql+aiomysql://root:123456@localhost:3306/FASTAPI_first?charset=utf8" async_engine = create_async_engine( ASYNC_DATABASE_URL, echo=True, #可选,输出sql日志 pool_size=10, # 设置连接池活跃的连接数 max_overflow=20, # 允许额外的连接数 ) AsynccsessionLocal = async_sessionmaker( bind=async_engine, # 绑定数据库引擎 class_=AsyncSession, # 指定会话类 expire_on_commit=False, # 提交后回话不过期,不会重新查询数据库 ) # 依赖项 async def get_database(): async with AsynccsessionLocal() as session: try: yield session #返回处理数据库会话给路由处理函数 await session.commit() # 提交事务 except Exception: await session.rollback() # 有异常,回滚 raise finally: await session.close() #关闭会话

第一个部分,先运行好数据库的服务,并得到url,在传入创建异步引擎的方法中,并配置一些参数;

第二部分就是创建会话工厂对象了,绑定数据库引擎后指定会话类,设置会话生命等参数后创建一个AsyncSession工厂实例,其被调用后会创建AsyncSession数据库会话对象。

重点是第三部分,就是依赖项,看到其逻辑是先异步创建一个会话对象,然后再返回处理数据库会话给路由处理函数,这里是什么意思呢?,我们结合下面的路由函数代码来讲一下:

@app.get("/book/get_book/{bookid}") async def get_book(book_id:int,db : AsyncSession = Depends(get_database)): result = await db.execute(select(Book).where(Book.id == book_id)) book = result.scalar_one_or_none() return book

这里的书籍查询路由函数就是使用了依赖注入get_database这个函数来实现对数据库中对应id书籍的信息的检索。下面是整个运行的时序:前端发送请求后,FastAPI 看到 db 参数是 Depends(get_database),就调用调用 get_database(),走到yield session这里时,将创建的session这个对象给到db,然后在get_book这个路由函数的方法体中就拿到数据库的会话对象,就可以对数据库进行增删改查的操作,在得到数据或者对数据库操作完、路由函数return返回值后,get_database才从ield 处恢复,执行await session.commit()提交事务到数据库,如果有异常则走await session.rollback()进行事务回滚。最终await session.close()关闭会话。这就实现了ORM在路由中的使用。

最后就一起看一个优雅的片段吧:

query=update(User).where(User.username == username).values(**user_data.model_dump( exclude_unset=True, exclude_none=True) )

这个片段实现了对用户信息的更新,因为用户信息不止一个字段,如果要一个个去更新的话会很麻烦,这里很巧妙的实现了信息更新:首先user_data是一个UserUpdateRequest类型的实例,这个类型的定义如下:

class UserUpdateRequest(BaseModel): nickname: str = None avatar: str = None gender: str = None bio: str = None phone: str = None

update(User).where(User.username == username)在通过用户名定位到对应用户对象在数据库中的位置后,首先对user_data这个实例调用model_dump将pydantic类型对象转换为字典,例如:{“nickname”: “小明”}这样的形式,后面的exclude_unset=True, exclude_none=True配置则是对用户没传或者传入的值是None则不转化,最后使用解包就可以实现对用户新赋值的信息的修改,而其他的信息保留的效果。比如:

.values({“nickname”: “小明”, “bio”: “你好”})

解开:

.values(nickname=“小明”, bio=“你好”)

在python中,对解包使用熟练的话会有一些妙用。

好了,这就是我在学习fastapi框架中遇到的难点以及一些特别的实现,还有一些特性由于作者原来学习过SpringBoot,所以理解和吸收起来没有太大难度,如果大家之前没接触过后端开发,学习的时候还是建议多花一些时间去理解框架中的功能或者特性是怎么实现的哦。因为作者刚开始就是学后端出身,很喜欢后端那种严丝合缝的逻辑感,如果有不懂的地方必须要弄懂才会踏实。我觉得在ai时代,理解内核原理不是无用之举,反而是提升自己的一种锻炼方式。这里是般逗,如果觉得本篇文章还可以,请大家留下一个赞,如有谬误,还请指正。我们下一篇再见!٩(ˊᗜˋ)و✧

RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进