Flask入坑记录(二) 整理项目结构

  • Post author:
  • Post category:其他




结构规划

上文我们已非常简陋的方式创建了一个Flask的WebApi应用,项目的结构如下图所示。

在这里插入图片描述

稍微有点编程经验的同学都会觉得这样的项目结构真的太简陋了,最简单的三层架构里面只有视图层(Controller),业务逻辑层,数据接入层都没有。项目中一点层次感都没有,那么我们应该如何调整项目的结构呢?



项目需要什么

Flask不像ASP.Net Core那样,创建好项目之后,该有的文件夹都替你创建好,项目的层次结构也比较清晰。在Flask里我们就需要自己做这项工作。

规划之前,首先我们要想清楚,一个小型的WebApi应用它需要有什么东西?

  • Controller模块(对外接口)
  • Model模块(业务逻辑)
  • Log模块(日志)
  • CORS(跨域)
  • JWT(Json Web Token)
  • Swagger(API在线文档)
  • Config(项目配置文件读写)



业务逻辑层

这里的业务逻辑层其实是一个非常庞大的概念,毕竟项目中的大部分时间我们都在对业务逻辑进行编码。所以这里有必要对其细分一下。



业务逻辑层的细分

其中,Model模块里面还能细分为:

  • 数据表实体类模块 db_model
  • 数据传输类模块(Data Transfer Object) dto_model
  • 提供数据接入层服务的Service模块
  • 业务逻辑处理模块 logic_models
  • 标准回送及各种错误码等定义

关于业务逻辑层(Model)这里可能会存在争议,有些小伙伴可能会认为数据库相关的东西应该单独抽离出来,或者业务逻辑上还能继续细分,每个开发人员都可能会根据自己的经验有不同的划分意见,这里仅仅是我的个人理解请不要过分深究。我的划分标准就是:只要跟业务逻辑有关系的代码,统统放到Model层,那如何定义”跟业务逻辑有关系”这个界限呢?我的理解是:只要在两个业务逻辑完全不同类型项目里,不能直接复制粘贴使用的都是”跟业务逻辑有关系”的代码。



请求处理流程

根据以上划分的业务逻辑层,我们现在可以先整理一下一个Http请求过来之后的处理流程。

请求处理流程

由上图可以看出,用户向Controller发起HTTP请求,Controller不对请求进行任何业务逻辑处理,直接丢给LogicModel(业务逻辑模块)处理,LogicModel负责对请求进行业务逻辑处理,处理时如果需要和数据库打交道,则调用提供数据接入层服务的Service模块,由Service模块统一进行数据库操作,最后返回结果。LogicModel返回给Controller一个标准统一的Result对象,Controller根据Result对象里的错误码(应用自定义的错误码),制作相应的HTTP回送(400,200等)返回给用户。



使用Flask的插件

正如上篇文章所提到的,Flask是一个插件非常丰富的框架,我们在上节中规划好的项目结构,大部分都能找到现成的插件。以下就是我们需要用到的插件整理。

插件名 用途
flask_jwt_extended 提供JWT验证
flasgger 提供Swagger的WebApi在线文档
flask_cors CORS跨域
flask_sqlalchemy 数据库ORM框架

如果需要连接MySQL数据库的话,还需要用到

pymysql

这个库作为Connector。



插件的安装

插件的安装和正常的Python包安装是一样的。

Windows下

pip install {插件名}

Linux下

pip3 install {插件名}



插件的初始化

我们安装完插件包之后,下一步就是要在项目中调用了。Flask常用的插件一般都会有两种初始化的方式:

  1. 通过构造函数传入Flask app对象初始化。
  2. 插件对象实例化之后通过init_app(Flask: app)方法初始化。

这里推荐使用第二种方法进行初始化,绝大部分的Flask插件都会有init_app这个方法。



整理结构

接下来就可以开始进入正题了,我们应该如何整理Flask项目的结构?在此之前还是要多费一点时间了解一下Python的模块,因为后续我们的项目大部分都是以模块为最小组成部分。



Python的模块

Python中的模块可以是一个py文件,也可以是一个文件夹。可以把Python的模块类比成C#里的命名空间,只要文件夹中有__init__.py这个文件,那么这个文件夹就是一个模块。有需要的小伙伴可以参考以下连接。


Python中的模块



消除循环依赖

有了模块的概念之后,细心的小伙伴可能会发现,Flask官方的Demo中引用其他插件其实是存在循环依赖的。Flask app需要引用其插件,而插件的初始化又需要引用Flask app,这就是一个循环依赖。

针对这种情况,我们可以把项目的结构设计成这样:

首先,项目应该有一个统一启动入口,在入口处我们创建Flask app实例,再将这个实例传到各个插件进行初始化。

DemoApp模块就是我们的Flask Api应用模块

DemoApp的__init__.py

# -*- coding: utf-8 -*-
from flask import Flask
from flask_cors import CORS
from .models import db_models
from . import log
from . import swagger
from . import jwt


def create_app():
    # 生成WebApp
    app = Flask(__name__)
    # 跨域
    CORS(app, supports_credentials=True)
    # 初始化数据库映射和链接
    db_models.init(app)
    # 初始化日志组件
    log.init()
    # 注册Controller
    __register_blueprint__(app)
    # 初始化jwt
    jwt.init(app)
    # 初始化Swagger
    swagger.init(app)
    return app


def __register_blueprint__(app: Flask):
    # 引用Controlls里面的蓝图并注册
    from .controllers.DemoController import route_demo
    app.register_blueprint(route_demo, url_prefix='/api')

注意这里引用的log、swagger、jwt、db_models模块都是对相应Flask插件的简单封装。以jwt为例。

自己封装的jwt模块中的__init__.py

# -*- coding: utf-8 -*-
from flask_jwt_extended import JWTManager
from flask import Flask

# 这个是插件的jwt实例
jwt = JWTManager()


def init(app: Flask):
    # JWT加密密钥
    app.config['JWT_SECRET_KEY'] = 'my-sectet'
    jwt.init_app(app)

总入口: run.py

# -*- coding: utf-8 -*-
import DemoApp

# 创建应用
app = DemoApp.create_app()


def main():
    # 运行在8848端口上并接受所有地址的请求
    app.run(host='0.0.0.0', port=8848)


if __name__ == '__main__':
    main()

到此为止,我们项目依赖关系如下: run.py依赖于DemoApp模块,DemoApp模块依赖于多个简单封装的Flask插件模块和若干业务逻辑模块。



项目结构概览

    |-- run.py                             // 总入口
    |-- DemoApp                            // WebApi程序
        |-- controllers                    // Controller模块
            |-- DomoController.py
            |-- ...
            |-- __init__.py
        |-- models                        // 业务逻辑模块
            |-- db_models                 // 数据库实体类模块
            |-- dto_models                // 数据传输实体模块
            |-- ...
            |-- __init__.py
        |-- jwt                          // JWT插件模块
        |-- log                          // 日志模块
        |-- swagger                      // Swagger在线Api文档模块
        |-- config                       // 程序相关配置模块
        |-- __init__.py



完整源码


GitHub_MyFlaskDemo



下节内容

下一节将针对以上提到的Flask插件进行踩坑记录。



版权声明:本文为weixin_38138153原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。