FastAPI:从 App.py 到模块化架构

FastAPI:从 App.py 到模块化架构

img

吧 使用 FastAPI 构建后端时,通常从单个app.py文件开始。虽然这种方法适用于小型项目,但随着应用程序的增长,维护和扩展很快就会变得具有挑战性。

在这篇博文中,我们将探讨如何将 FastAPI 应用程序从单片app.py文件重构为由路由器控制器、服务存储库组成的更结构化的架构。

介绍:我们的 Todo API

在深入重构过程之前,让我们先看一下要使用的 API。我们正在构建一个简单的 Todo 应用程序,其中包含以下端点:

img

这些 API 将允许用户对待办事项执行 CRUD(创建、读取、更新、删除)操作。每个待办事项将具有以下属性:

**id**:待办事项的唯一标识符

**title**:待办事项的标题或描述

**completed**:布尔值,表示待办事项是否已完成

现在我们了解了我们正在使用的 API,让我们看看一些先决条件以及如何实现它们。

先决条件

在我们开始初始方法和重构之前,让我们先设置我们的 FastAPI 项目。

安装 Python

设置并激活虚拟环境

python3 -m venv venv 
source  env /bin/activate   # 在 Windows 上使用 `env\Scripts\activate`

创建**requirements.txt**FastAPI 和 Uvicorn 并将其添加到依赖项列表

fastapi 
uvicorn

安装依赖项

pip3 安装 -r 要求.txt

初步方法:app.py 中的所有内容

app.py让我们从一个完全在根级别实现的简单 Todo API 开始:

从fastapi导入FastAPI、HTTPException
从pydantic导入BaseModel 

app = FastAPI() 

# 用于创建新待办事项的 Pydantic 模型
class  TodoCreate ( BaseModel ): 
    title: str 

# 待办事项的 Pydantic 模型,继承自 TodoCreate 并添加 id 和 done 字段
class  Todo ( TodoCreate ): 
    id : int
     done: bool = False 

# 待办事项的内存存储(模拟数据库)
 todos = [] 

# 创建新待办事项的端点
@app.post( "/todos" , response_model=Todo ) 
def  create_todo ( todo: TodoCreate ): 
    # 创建一个带有递增 id 的新待办事项
    new_todo = Todo( id = len (todos) + 1 , **todo.model_dump()) 
    todos.append(new_todo)   # 将新的待办事项添加到列表中
    return new_todo   # 将创建的待办事项作为响应返回

# 端点获取所有待办事项
@app.get( "/todos" , response_model= list [Todo] ) 
def  get_todos (): 
    return todos   # 返回待办事项列表作为响应

# 通过 id 获取特定待办事项的端点
@app.get( "/todos/{todo_id}" , response_model=Todo ) 
def  get_todo ( todo_id: int ): 
    for todo in todos: 
        if todo. id == todo_id:
            返回todo   # 如果找到则返回 todo 
    # 如果未找到 todo,则引发带有 404 状态代码和消息的 HTTPException 
    raise HTTPException(status_code= 404,detail = “Todo not found”)

# 通过 id 更新 todo 的端点
@app.put(“/todos/{todo_id}”,response_model=Todo)
def  update_todo(todo_id:int,updated_todo:TodoCreate):
    for todo in todos:
        if todo.id == todo_id:
            todo.title = updated_todo.title   # 更新 todo 的标题
            返回todo   # 返回更新后的 todo 
    # 如果未找到 todo,则引发带有 404 状态代码和消息的 HTTPException raise HTTPException 
    ( status_code= 404 , detail= "Todo not found" ) 

# 通过 id 删除 todo 的端点
@app.delete( "/todos/{todo_id}" ) 
def  delete_todo ( todo_id: int ): 
    for index, todo in  enumerate (todos): 
        if todo. id == todo_id: 
            del todos[index]   # 从列表中删除待办事项
            return { "message" : "待办事项已成功删除" }   # 返回成功消息
    # 如果未找到待办事项,则引发带有 404 状态代码和消息的 HTTPException 
    raise HTTPException(status_code= 404 , detail= "未找到待办事项" ) 

# 使用 Uvicorn 服务器运行应用程序的主块
if __name__ == "__main__" : 
    import uvicorn 
    uvicorn.run( "app:app" , port= 3000 , host= "0.0.0.0" , reload= True )

Pydantic 是一个 Python 数据验证和解析库,它使用 Python 类型注释来定义和验证数据结构。它通过提供自动类型检查和解析来确保数据完整性,从而更轻松地处理结构化数据。

要启动 API,我们使用命令***,******python3 app.py\***

唔…

虽然这种方法适用于小型应用程序,但它有几个缺点:

  1. 所有路由、业务逻辑和数据存储都混合在一个文件中。
  2. 随着应用程序的增长,维护和扩展变得困难。
  3. 测试单个组件变得具有挑战性
  4. 代码可重用性有限

img

让我们开始重构之旅……

路由器简介

构建我们的应用程序的第一步是引入路由器。

API 中的路由器将相关端点(路由)组织和分组到单独的模块或文件中,从而增强代码模块化和结构化。

创建一个名为的新目录routers并添加一个名为todo_router.py

从fastapi导入APIRouter 

router = APIRouter() 

@router.post( "/todos" ) 
def  create_todo (): 
    pass 

@router.get( "/todos" ) 
def  get_todos (): 
    pass 

@router.get( "/todos/{todo_id}" ) 
def  get_todo ( todo_id: int ): 
    pass 

@router.put( "/todos/{todo_id}" ) 
def  update_todo ( todo_id: int ): 
    pass 

@router.delete( "/todos/{todo_id}" ) 
def  delete_todo ( todo_id: int ): 
    pass

现在更新app.py以使用路由器,

从fastapi导入FastAPI
从路由器导入todo_router 

app = FastAPI() 

app.include_router(todo_router.router)

如果__name__ == "__main__" :
    导入uvicorn 
    uvicorn.run( "app:app" , port= 3000 , host= "0.0.0.0" , reload= True )

通过引入路由器,我们将待办事项相关的路由与主app.py文件分开,使其更清晰、更集中。

添加控制器

接下来,我们将引入控制器来处理请求处理逻辑。

控制器处理传入的请求,协调应用程序的逻辑,并管理 API 路由(端点)和服务层之间的数据流。

创建一个名为的新目录controllers并添加一个名为todo_controller.py

从fastapi导入HTTPException
从pydantic导入BaseModel

类 TodoCreate(BaseModel):
    title:str

类 Todo(TodoCreate):
    id:int
    已完成:bool = False

类 TodoController:
    def  __init__(self):
        self.todos = [] 

    def  create_todo(self,todo:TodoCreate):
        new_todo = Todo(id = len(self.todos)+ 1,**todo.model_dump())
        self.todos.append(new_todo)
        返回new_todo 

    def  get_todos(self):
        返回self.todos 

    def  get_todo(self,todo_id:int):
        对于self.todos中的todo :
            如果todo。id == todo_id:
                返回todo
        引发HTTPException(status_code= 404 , detail= "未找到 Todo" ) 

    def  update_todo ( self, todo_id: int , updated_todo: TodoCreate ): 
        for todo in self.todos:
            如果todo. id == todo_id: 
                todo.title = updated_todo.title
                返回todo
        引发HTTPException(status_code= 404 , detail= "未找到 Todo" ) 

    def  delete_todo ( self, todo_id: int ): 
        for index, todo in  enumerate (self.todos):
            如果todo. id == todo_id: 
                del self.todos[index]
                返回{ "message" : "已成功删除 Todo" }
        引发HTTPException(status_code= 404 , detail= "未找到 Todo" )

更新todo_router.py以使用控制器,

from fastapi import APIRouter 
fromcontrollers.todo_controller import TodoController, TodoCreate, Todo 

router = APIRouter() 
todo_controller = TodoController() 

@router.post( " / todos" , response_model=Todo ) 
def  create_todo ( todo: TodoCreate ): 
    return todo_controller.create_todo (todo) 

@router.get( "/todos" , response_model= list [Todo] ) 
def  get_todos (): 
    return todo_controller.get_todos() 

@router.get( "/todos/{todo_id}" , response_model=Todo ) 
def  get_todo ( todo_id: int ): 
    return todo_controller.get_todo(todo_id) 

@router.put( "/todos/{todo_id}" , response_model=Todo ) 
def  update_todo ( todo_id:int,updated_todo:TodoCreate):
    返回todo_controller.update_todo(todo_id,updated_todo)@router.delete 

(“/todos/{todo_id}”)
defdelete_todo (todo_id :int):
    返回todo_controller.delete_todo(todo_id)

实现服务层

现在,让我们引入一个服务层来处理业务逻辑。

服务包含 API 的核心业务逻辑,实现应用程序所需的特定功能和操作。它们从 API 端点抽象出复杂的业务规则。

创建一个名为的新目录services并添加一个名为todo_service.py

from pydantic import BaseModel 

class  TodoCreate ( BaseModel ): 
    title: str 

class  Todo ( TodoCreate ): 
    id : int
     Completed: bool = False 

class  TodoService : 
    def  __init__ ( self ): 
        self.todos = [] 

    def  create_todo ( self, todo: TodoCreate ) ) -> Todo: 
        new_todo = Todo( id = len (self.todos) + 1 , **todo.model_dump()) 
        self.todos.append(new_todo) 
        return new_todo 

    def  get_todos ( self ) ->列表[Todo]:
        返回self.todos 

    def  get_todo ( self, todo_id: int ) -> Todo |无:
        用于self.todos中的待办事项:
            如果待办事项。id == todo_id:
                返回todo
        返回 None 

    def  update_todo ( self,todo_id:int,updated_todo:TodoCreate ) -> Todo | None:
        for todo in self.todos:
            如果todo.id == todo_id:
                todo.title = updated_todo.title
                返回todo
        返回 None 

    def  delete_todo ( self, todo_id: int ) -> bool : 
        for index, todo in  enumerate (self.todos): 
            if todo. id == todo_id: 
                del self.todos[index]
                返回 True
        返回 False

更新todo_controller.py以使用该服务,

from fastapi import HTTPException 
from services.todo_service import TodoService, TodoCreate, Todo 

class  TodoController : 
    def  __init__ ( self ): 
        self.todo_service = TodoService() 

    def  create_todo ( self, todo: TodoCreate ): 
        return self.todo_service.create_todo(todo) 

    def  get_todos ( self ): 
        return self.todo_service.get_todos() 

    def  get_todo ( self, todo_id: int ): 
        todo = self.todo_service.get_todo(todo_id)
        如果todo为 None : 
            raise HTTPException(status_code= 404 ,detail= "Todo not find" ) 
        return todo 

    def  update_todo ( self, todo_id: int , Updated_todo: TodoCreate ): 
        todo = self.todo_service.update_todo(todo_id, Updated_todo)
        如果todo为 None : 
            raise HTTPException(status_code = 404,detail = “未找到Todo”)
        返回todo 

    def  delete_todo(self,todo_id:int):
        if self.todo_service.delete_todo(todo_id):
            return { “message”:“Todo已成功删除” } 
        raise HTTPException (状态码 = 404,详细信息 = “未找到待办事项”)

创建存储库层

最后,让我们引入一个存储库层来处理数据持久性。

存储库为数据持久性操作提供了一个抽象层,封装了与数据库或外部数据源的交互。它们提供了存储、检索、更新和删除数据的标准化方法。

创建一个名为的新目录repositories并添加一个名为todo_repository.py

from pydantic import BaseModel 

class  TodoCreate ( BaseModel ): 
    title: str 

class  Todo ( TodoCreate ): 
    id : int
     Completed: bool = False 

class  TodoRepository : 
    def  __init__ ( self ): 
        self.todos = [] 

    def  create_todo ( self, todo: TodoCreate ) ) -> Todo: 
        new_todo = Todo( id = len (self.todos) + 1 , **todo.model_dump()) 
        self.todos.append(new_todo) 
        return new_todo 

    def  get_todos ( self ) ->列表[Todo]:
        返回self.todos 

    def  get_todo ( self, todo_id: int ) -> Todo |无:
        用于self.todos中的待办事项:
            如果待办事项。id == todo_id:
                返回todo
        返回 None 

    def  update_todo ( self,todo_id:int,updated_todo:TodoCreate ) -> Todo | None:
        for todo in self.todos:
            如果todo.id == todo_id:
                todo.title = updated_todo.title
                返回todo
        返回 None 

    def  delete_todo ( self, todo_id: int ) -> bool : 
        for index, todo in  enumerate (self.todos): 
            if todo. id == todo_id: 
                del self.todos[index]
                返回 True
        返回 False

更新todo_service.py以使用存储库,

from repositories.todo_repository import TodoRepository, TodoCreate, Todo 

class  TodoService : 
    def  __init__ ( self ): 
        self.todo_repository = TodoRepository() 

    def  create_todo ( self, todo: TodoCreate ) -> Todo: 
        return self.todo_repository.create_todo(todo) 

    def  get_todos ( self ) ->列表[Todo]: 
        return self.todo_repository.get_todos() 

    def  get_todo ( self, todo_id: int ) -> Todo |无:
        返回self.todo_repository.get_todo(todo_id) 

    def  update_todo ( self, todo_id: int , Updated_todo: TodoCreate ) -> Todo | None :
        返回self.todo_repository.update_todo(todo_id, Updated_todo) 

    def  delete_todo ( self, todo_id: int ) -> bool :
        返回self.todo_repository.delete_todo(todo_id)

我们的重构之旅到此结束…

img

在从单体架构转变为使用 FastAPI 的结构化模块化架构的过程中app.py,我们将 Todo API 转变为更具可扩展性和可维护性的应用程序。通过采用路由器、控制器、服务和存储库,我们实现了更清晰的关注点分离,并增强了我们在项目发展过程中管理复杂性的能力。

模块化架构的主要优点:

  1. **提高可维护性:**每个组件(路由器、控制器、服务和存储库)现在都处理特定的职责,从而降低了进行更改时产生意外副作用的风险。
  2. **增强的可测试性:**有了不同的层次,单元测试变得更加简单。我们可以单独测试每个组件,确保整个应用程序的稳健性和可靠性。
  3. **可扩展性和灵活性:**模块化设计更易于扩展。无需对整个代码库进行大量重新设计,即可添加新功能或修改现有功能。这种灵活性可以无缝切换数据库或更新业务逻辑。

我们的存储库现在如下所示…

img

通过将我们的 FastAPI 应用程序重构为模块化架构,我们为持续增长和灵活性奠定了坚实的基础。这种方法不仅增强了我们当前的开发工作,还为我们应对未来的挑战和机遇做好了准备。

采用模块化不仅仅意味着组织代码——它还意味着培育一种高效、协作和持续改进的文化。

性的能力。

模块化架构的主要优点:

  1. **提高可维护性:**每个组件(路由器、控制器、服务和存储库)现在都处理特定的职责,从而降低了进行更改时产生意外副作用的风险。
  2. **增强的可测试性:**有了不同的层次,单元测试变得更加简单。我们可以单独测试每个组件,确保整个应用程序的稳健性和可靠性。
  3. **可扩展性和灵活性:**模块化设计更易于扩展。无需对整个代码库进行大量重新设计,即可添加新功能或修改现有功能。这种灵活性可以无缝切换数据库或更新业务逻辑。

我们的存储库现在如下所示…

[外链图片转存中…(img-DNEVyXCt-1722244113558)]

通过将我们的 FastAPI 应用程序重构为模块化架构,我们为持续增长和灵活性奠定了坚实的基础。这种方法不仅增强了我们当前的开发工作,还为我们应对未来的挑战和机遇做好了准备。

采用模块化不仅仅意味着组织代码——它还意味着培育一种高效、协作和持续改进的文化。

再见!!

博客原文:专业人工智能社区

  • 5
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值