测试需求平台9:数据持久化与PyMySQL使用

2023-10-21 19:19:30 浏览数 (1)

✍ 此系列为整理分享已完结入门搭建《TPM提测平台》系列的迭代版,拥抱Vue3.0将前端框架替换成字节最新开源的arco.design,其中约60%重构和20%新增内容,定位为从 0-1手把手实现简单的测试平台开发教程,内容将囊括基础、扩展和实战,由浅入深带你实现测试开发岗位中平台工具技术能力入门和提升。

本篇需要提前准备的环境和开发内容:

  • 准备数据库,Mysql5.7 本地或云服务均可
  • 实现后端接口服务的数据库操作

产品数据持久化

在项目管理中,真正的数据需要持久化操作的,这里必然就离不开数据库,本项目使用的Mysql数据库,但不会过多的讲解SQL的内容,只会重点讲解后端服务中Python对于数据库的操作相关知识点。

数据库和产品表初始化

使用数据库IDE工具链接mysql数据库,并创建一个数据库TPMStore和一个Products表,字段分别如下

使用Navicat可视化创建,或查看笔者大奇之前分享过的一个好用的开源Beekeeper工具

这里给出SQL语句方便进行表格创建,顺便添加两条正式测试数据

代码语言:javascript复制
SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;

-- ----------------------------
-- Table structure for products
-- ----------------------------
DROP TABLE IF EXISTS `products`;
CREATE TABLE `products` (
  `id` bigint NOT NULL AUTO_INCREMENT COMMENT '编号自增',
  `keyCode` varchar(200) NOT NULL COMMENT '项目唯一编号',
  `title` varchar(200) NOT NULL COMMENT '中文项目名',
  `desc` varchar(500) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL DEFAULT '' COMMENT '描述',
  `operator` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '操作者',
  `update` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '操作时间',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='项目产品表';

-- ----------------------------
-- Records of products
-- ----------------------------
BEGIN;
INSERT INTO `products` VALUES (1, 'data', '数据大盘', '内部一个数据技术分析的项目,用于分析各种数据聚合平台', 'daqi', '2021-07-17 20:38:37');
INSERT INTO `products` VALUES (2, 'payment', '收银台', '支付聚合收银台', 'lili', '2021-07-17 20:40:29');
COMMIT;

SET FOREIGN_KEY_CHECKS = 1;

产品查询接口改造

之前的接口 /api/product/list 是硬编码返回,我们这里需要改造成通过数据库查询获得。

python实现mysql的数据的方式目前支持度较好的有:

  • mysqlclient (Star 2.1K )
  • PyMySQL(Star 7K )
  • mysql.connector (Mysql官方的驱动库)

以上 github star 数据统计于 2022/07/10

综合使用度和后续可能使用ORM(对象关系映射)优化,本项目选择PyMySQL

代码语言:javascript复制
# 安装依赖包
python3 -m pip install PyMySQL

然后主要就是引入包,实现数据库的连接和查询操作

代码语言:javascript复制
# -*- coding:utf-8 -*-
from flask import Blueprint
import pymysql.cursors

app_product = Blueprint("app_product", __name__)

# 使用用户名密码创建数据库链接
connection = pymysql.connect(host='localhost',   # 数据库IP地址或链接域名
                             user='root',     # 设置的具有增改查权限的用户
                             password='******', # 用户对应的密码
                             database='TPMStore',# 数据表
                             charset='utf8mb4',  # 字符编码
                             cursorclass=pymysql.cursors.DictCursor) # 结果作为字典返回游标

@app_product.route("/api/product/list",methods=['GET'])
def product_list():
    # 使用python的with..as控制流语句(相当于简化的try except finally)
    with connection.cursor() as cursor:
        # 查询产品信息表-按更新时间新旧排序
        sql = "SELECT * FROM `Products` ORDER BY `Update` DESC"
        cursor.execute(sql)
        data = cursor.fetchall()

    # 按返回模版格式进行json结果返回
    resp_data = {
        "code": 20000,
        "data": data
    }
    return resp_data

对改造的后的接口用Postman做个验证测试,从下图可以看到数据已经是从product表里查出来的。

PyMySQL使用

PyMySQL 是在 Python3.x 版本中用于连接 MySQL 服务器的一个库。

  • 官方文档 https://pymysql.readthedocs.io
  • 菜鸟教程:https://www.runoob.com/python3/python3-mysql.html

依赖安装

首先关于安装依赖,上边已经给列出通过$ python3 -m pip install PyMySQL 命令,这里需要特别强调一下,如果你使用的是Mysql 8.x 数据库服务,由于高版本改变了密码加密方式,所以必须安装额外的依赖

代码语言:javascript复制
# 不兼容加密方式连接报错如下
# RuntimeError: 'cryptography' package is required for sha256_password or caching_sha2_password auth methods
$ python3 -m pip install PyMySQL[rsa]

另外还可以通过改mysql的加密方式为 mysql_native_password,笔者本地版本8.0.25为了方便已经修改了,查询的方法见截图:

数据库连接

连接实例的是在代码中import pymysql 后通过.connet(...)方法创建,扒一下源码可以看到有很到参数

这里捡一些基本的和可能用到的参数做下解释说明

  • host 数据库地址IP或者域名
  • user 数据库分配的账号
  • password 数据库分配的密码
  • database 指定数据库, 可以为None后续使用时候进行切换
  • port 数据库端口号,默认3306
  • read_timeout 读操作超时时间
  • write_timeout 写操作超时时间
  • charset 字符编码中文建议设置utf8等
  • cursorclass 设置返回的数据类型,默认查询返回的数据是tuples元组,一般我们数据多数以table的形式呈现,所以建议设置成cursorclass=MySQLdb.cursors.DictCursor字典类型h
  • connect_timeout 连接超时时间

还有其他如ssl加密相关、连接数设置等,实际项目中按需查阅参数配置。

另外一点在创建db对象后,其实就可以通过游标创建对应的数据库以及切换对应的库。

代码语言:javascript复制
# 执行创建数据库
cursor.execute("CREATE DATABASE QiDBTest character SET utf8mb4;") 

# 切库或使用 db.select_db("数据库名")
cursor.execute("use QiDBTest;") 

# #可以通过以下语句进行创建查询
# cursor.execute("SHOW DATABASES;")
# print(cursor.fetchall())

数据库表操作

创建数据库连接对象,然后再创建一个游标对象cursor,通过cursor.excute() 执行对应的语句,就可以进行表相关、数据相关操作,其实excute的操作,你完全可以被看做使用任何一个数据库IDE工具,打开了一个查询面板来执行对应的SQL语句

表创建和数据查询 均通过执行对应的SQL语句实现,其中查询结果还需要通过cursor.fetchall()获取,对应的还有两个常用的

  • cursor.fetchone() 返回一行数据
  • cursor.fetchmany(size) 返回指定数量行
代码语言:javascript复制
import pymysql
 
# 创建数据库连接
db = pymysql.connect(host='localhost',
                     user='mrzcode',
                     password='mrzcode',
                     database='QiDBTest')
 
# 使用cursor()方法创建一个游标对象 cursor
cursor = db.cursor()

# 使用execute()方法执行 SQL,如果表存在则删除
cursor.execute("DROP TABLE IF EXISTS QiTableDemo")

# 创建表语句
sqlCreateTable = """
    CREATE TABLE `QiTableDemo` (
        `id` bigint NOT NULL AUTO_INCREMENT COMMENT '自增ID',
        `name` varchar(100) NOT NULL COMMENT '名称',
        `desc` varchar(500) COMMENT '描述',
         PRIMARY KEY (`id`)
    )"""

# 执行SQL语句创建表
cursor.execute(sqlCreateTable)

# 验证查询下表情况
cursor.execute("SHOW TABLES;")
print(cursor.fetchall())  # (('qitabledemo',),) 默认元组查询表列表


# 查询QiTableDemo表所有数据
sqlSelect = "SELECT * FROM QiTableDemo;" 

# 执行表查询语句
cursor.execute(sqlSelect)
print(cursor.fetchall())  # () 新表返回一个空的tuples

表数据增删改 额外在execute基础上进行db.commit()提交,如果不提交连接关闭后这些数据修改是不生效的。

代码语言:javascript复制
import pymysql

# 创建数据库连接
db = pymysql.connect(host='127.0.0.1',
                     user='root',
                     password='rox@123',
                     database='QiDBTest',
                     cursorclass=pymysql.cursors.DictCursor)

# 使用 cursor() 方法创建一个游标对象 cursor
cursor = db.cursor()

sqlInsert = '''INSERT INTO qitabledemo(`name`,`desc`) VALUES ('插入测试名称', '插入测试描述');'''

# 执行表查询语句
cursor.execute(sqlInsert)

# 对执行提交,这里可以尝试注释掉验证不进行提交数据能否插入
db.commit()

# 查询数据是否正确插入
cursor.execute("select * from qitabledemo;")
print(cursor.fetchall()) # [{'id': 6, 'name': '插入测试名称', 'desc': '插入测试描述'}]

# 关闭数据库连接
db.close()

上边这种sql语句是一个字符串形式,但实际在代码逻辑处理中值一般都是通过变量传递的,所以通过以下两种方式动态赋值

代码语言:javascript复制
# 方式一:占位拼接字符串
sqlMethod1 = "INSERT INTO qitabledemo(`name`,`desc`) VALUES ('%s', '%s')" % ("占位名称","占位描述")
cursor.execute(sqlMethod1)

# 方式二:通过execute传参
sqlMethod2 = "INSERT INTO qitabledemo(`name`,`desc`) VALUES ('%s', '%s')"
cursor.execute(sqlMethod1,(变量1,变量2))

剩下关于更新、删除的操作同样,只是按需替换成对应的语句,但强调的一点是不要忘记commit,以下给出我这边的例子和验证测试

事务和错误处理

关于事务机制 可以确保数据一致性,场景主要用于多逻辑交互时候其中操作错误,进行响应的回滚处理,避免产生脏数据,事务通常具有4个属性:原子性、一致性、隔离性、持久性。 对于支持事务的数据库, 在Python数据库编程中,当游标建立之时,就自动开始了一个隐形的数据库事务。

  • commit() 方法游标的所有更新操作;
  • rollback() 方法回滚当前游标的所有操作。

每一个方法都开始了一个新的事务。

代码语言:javascript复制
sql = "数据库操作的增删改查语句"
try:
   # 执行SQL语句
   cursor.execute(sql)
   # 向数据库提交
   db.commit()
except:
   # 发生错误时回滚
   db.rollback()

关于错误 DB API中定义了一些数据库操作的错误及异常(以下引用菜鸟教程),严谨的编程需要对不同的错误进行响应的处理。

异常

描述

Warning

当有严重警告时触发,例如插入数据被截断等等。必须是 StandardError 的子类。

Error

警告以外所有其他错误类。必须是 StandardError 的子类。

InterfaceError

当有数据库接口模块本身的错误(而不是数据库的错误)发生时触发。必须是Error的子类。

DatabaseError

和数据库有关的错误发生时触发。必须是Error的子类。

DataError

当有数据处理时的错误发生时触发,例如:除零错误,数据超范围等等。必须是DatabaseError的子类。

OperationalError

指非用户控制的,而是操作数据库时发生的错误。例如:连接意外断开、 数据库名未找到、事务处理失败、内存分配错误等等操作数据库发生的错误。必须是DatabaseError的子类。

IntegrityError

完整性相关的错误,例如外键检查失败等。必须是DatabaseError子类。

InternalError

数据库的内部错误,例如游标(cursor)失效了、事务同步失败等等。必须是DatabaseError子类。

ProgrammingError

程序错误,例如数据表(table)没找到或已存在、SQL语句语法错误、 参数数量错误等等。必须是DatabaseError的子类。

NotSupportedError

不支持错误,指使用了数据库不支持的函数或API等。例如在连接对象上 使用.rollback()函数,然而数据库并不支持事务或者事务已关闭。必须是DatabaseError的子类。

新手操作指南

最后总结一下一般Python使用PyMySQL的编程步骤

  1. 引用模块库
  2. 创建连接对象db=connect(...)
  3. 从连接对象获取游标cursor=db.cursor()
  4. 准备sql语句并通过游标执行cursor.execute(sql)
  5. 如果是非查询动作还需要db.commit()
  6. 关闭数据库连接db.close()

以上就是本篇的主要内容,重点讲解Python 对mysql数据库的操作,并且开始就开门见山地做个了项目实战,相信这些内容掌握了,本系列项目中有关数据操作部分都会游刃有余。

上篇回顾:测试需求平台8:Acro Vue页面创建及菜单路由讲解

下篇预告:产品服务管理接口实现

0 人点赞