✍ 此系列为整理分享已完结入门搭建《TPM提测平台》系列的迭代版,拥抱Vue3.0将前端框架替换成字节最新开源的arco.design,其中约60%重构和20%新增内容,定位为从 0-1手把手实现简单的测试平台开发教程,内容将囊括基础、扩展和实战,由浅入深带你实现测试开发岗位中平台工具技术能力入门和提升。
基于前几篇有关Flask API使用教程、Blueprint路由优化、数据持久化PyMySQL使用的知识内容,本篇就可以很轻松的实现《测试需求平台》中产品模块管理所需要的所有接口服务了。
封装数据连接
在正式过实现接口服务之前,我们需要先封装下之前做数据连接操作写在外边的对象代码,因为多方法中如增、改、删使用完后会关闭数据库连接,所以不能一次声明到处使用,我们需要在每次接口请求处理业务前,进行新的数据库初始化,保证数据库连接实例可用。
代码语言:javascript复制# 数据库连接方法
def connectDB():
connection = pymysql.connect(host='127.0.0.1',
user='mrzcode',
password='mrzcode',
database='TPMStore',
charset='utf8mb4',
cursorclass=pymysql.cursors.DictCursor)
# 返回新数据库连接对象
return connection
最简单的方式就是提取出一个公共方法,每个接口方法实现的时候只需调用重新实例化即可,这个定义就直接在product.py
顶部实现。
当然对于数据管理还可以提取配置文件、使用连接池等方式进一步优化,不过学习总要有个循序渐进的过程,当前先卖个关子,后续会详细讲到。
产品管理接口
添加接口
基于之前实现的产品查询接口类 product.py 实现产品信息添加接口,基础关键定义如下:
- methods 定义为POST请求
- flask request模块的get_data()获取body
- json 请求处理请求的JSON格式数据
新增依赖引用,其中json是一种轻量级的数据交换格式。
代码语言:javascript复制from flask import request
import json
在实现产品新增信息落库之前要增加一个查询判断是否已经存在的逻辑,需求上定义keyCode是关键词,名称可以相同不做特殊处理,如果重复给出提示,code 定义20001,代码中千万不要忘记上节数据操作的commit
动作。
# [POST方法]实现新建数据的数据库插入
@app_product.route("/api/product/create",methods=['POST'])
def product_create():
# 初始化数据库链接
connection = connectDB()
# 定义默认返回结构体
resp_data = {
"code": 20000,
"message": "success",
"data": []
}
# 获取请求传递json body
body = request.get_data()
body = json.loads(body)
with connection:
# 先做个查询,判断keyCode是否重复(这里的关键词最初定义为唯一项目编号或者为服务的应用名)
with connection.cursor() as cursor:
select = "SELECT * FROM `products` WHERE `keyCode`=%s"
cursor.execute(select, (body["keyCode"],))
result = cursor.fetchall()
# 有数据说明存在相同值,封装提示直接返回
if len(result) > 0:
resp_data["code"] = 20001
resp_data["message"] = "唯一编码keyCode已存在"
return resp_data
with connection.cursor() as cursor:
# 拼接插入语句,并用参数化%s构造防止基本的SQL注入
# 其中id为自增,插入数据默认数据设置的当前时间
sql = "INSERT INTO `products` (`keyCode`,`title`,`desc`,`operator`) VALUES (%s,%s,%s,%s)"
cursor.execute(sql, (body["keyCode"], body["title"], body["desc"], body["operator"]))
# 提交执行保存插入数据
connection.commit()
# 按返回模版格式进行json结果返回
return resp_data
以上编写完成再次用ide或者命令运行 app.py 启动后端程序,用postman接口测试工具进行请求测试,验证产品新增接口服务两种逻辑情况
1)符合条件请求验证数据正确落库
2)KeyCode已经存在验证返回提示
特别说明一下,本系列练手项目不会过多做比如边界值、接口参数缺失等这类信息,毕竟前后端都一个人开发,配合约定有一端处理不出现上述情况即可,不过如果在实际工作项目中多人开发的时候,后端的接口相关的必要验证处理还是建议写的。
修改接口
产品信息修改与插入的代码逻辑几乎一样,只是请求Body需要而外带过来插入自动生成的ID,并将语句换成了UPDATE
语法。
# [POST方法]根据项目ID进行信息更新
@app_product.route("/api/product/update",methods=['POST'])
def product_update():
# 按返回模版格式进行json结果返回
resp_data = {
"code": 20000,
"message": "success",
"data": []
}
# 获取请求传递json
body = request.get_data()
body = json.loads(body)
# 初始化数据库链接
connection = connectDB()
with connection:
with connection.cursor() as cursor:
# 查询需要过滤状态为有效的
select = "SELECT * FROM `products` WHERE `keyCode`=%s AND `status`=0"
cursor.execute(select, (body["keyCode"],))
result = cursor.fetchall()
# 有数据并且不等于本身则为重复,封装提示直接返回
if len(result) > 0 and result[0]["id"] != body["id"]:
resp_data["code"] = 20001
resp_data["message"] = "唯一编码keyCode已存在"
return resp_data
# 如果没有重复,定义新的链接,进行更新操作
with connection.cursor() as cursor:
# 拼接更新语句,并用参数化%s构造防止基本的SQL注入
# 条件为id,更新时间用数据库NOW()获取当前时间
sql = "UPDATE `products` SET `keyCode`=%s, `title`=%s,`desc`=%s,`operator`=%s, `update`= NOW() WHERE id=%s"
cursor.execute(sql, (body["keyCode"], body["title"], body["desc"], body["operator"], body['id']))
# 提交执行保存更新数据
connection.commit()
return resp_data
修改逻辑中也需要对KeyCode做重复校验,如果需求上定义KeyCode不可以修改,那么只需要前端在修改的时候处理成置灰不可更改就行。另外查询的时候拿去数据我用了是fetchall()
做的循环判定,这里可以简化为fetchone()
,毕竟在插入的时候已经做了防重限制,正常情况下只会出现空或一条数据。
同样进行代码逻辑的测试,这里首先演示上说的没做参数校验是啥情况的返回,明显的代码错误在body["id"]获取的时候异常,直接返回了框架Traceback 错误。
此处是独立的更新接口,因为id是修改操作where关键,笔者是推荐先做一步id参数是否为空的校验,如果是你应该怎么优化呢?提示可以使用 if ('key' not in body):
做逻辑判断。
对于修改这里只验证下正确修改的情况
删除接口
对于产品列表的删除操作,可以通过硬删除和软删除来实现,前者就是真正DELETE
,后者是对其表增加一个状态字段,标记某状态为删除状态,在查询接口中需要通过条件查询排除此状态。
硬删除接口
按照标准的RefAPI,通过定义methods = delete
方法定义请求接口,参数只需要对应数据的id,于此同时因为id是删除的操作唯一条件,所以代码逻辑中有必要增一项非空判断。
# [DELETE方法]根据id实际删除项目信息
@app_product.route("/api/product/delete", methods=['DELETE'])
def product_delete():
# 返回的reponse
resp_data = {
"code": 20000,
"message": "success",
"data": []
}
# 通过params 获取id
ID = request.args.get('id')
# 做个参数必填校验
if ID is None:
resp_data["code"] = 20002
resp_data["message"] = "请求id参数为空"
return resp_data
# 重新链接数据库
connection = connectDB()
with connection.cursor() as cursor:
sql = "DELETE from `products` where id=%s"
cursor.execute(sql, ID)
connection.commit()
return resp_data
软删除接口
在通常的业务操作中数据都不是真的删除的,尤其像产品/项目这种会有下游依赖的数据,一般做法都是表数据增加对应的状态字段,用数字或者字符表示状态,所需要做的操作就是“删除”触发的是更新操作,在需求交互上我们叫“停用”更为合适一些,这也就是所谓的软删除,仅标记状态不做实际数据删除。
1)先要对products表增加一个状态字段,执行中执行修改命令,或使用IDE直接添加均可。
代码语言:javascript复制alter table products add status int default 0 not null comment '状态有效0,无效1' after `desc`;
2)定义的新/api/product/remove
接口名,参考修改和硬删除代码实现软删除接口
# [POST方法]根据id更新状态项目状态,做软删除
@app_product.route("/api/product/remove", methods=['POST'])
def product_remove():
# 返回的reponse
resp_data = {
"code": 20000,
"message": "success",
"data": []
}
ID = request.args.get('id')
# 做个参数必填校验
if ID is None:
resp_data["code"] = 20002
resp_data["message"] = "请求id参数为空"
return resp_data
# 重新链接数据库
connection = connectDB()
# 更改操作
with connection.cursor() as cursor:
# 状态默认正常状态为0,删除状态为1
# alter table products add status int default 0 not null comment '状态有效0,无效0' after `desc`;
sql = "UPDATE `products` SET `status`=1 WHERE id=%s"
cursor.execute(sql, ID)
connection.commit()
return resp_data
以上两个接口就不做测试演示了,大家自行做下验证测试。
Python with语句
扩展了解一个python知识点,从上边的所有代码中可以看到,通篇数据操作的没有进行db.close()
操作,而是用了with as
,它的基本思想是with所求值对象必须有一个enter()方法,一个exit()方法。应用到数据库操作实例中就是实现了执行退出后关闭具柄,python最常见用语文件处理,举个对比实例:
# 带有异常捕获文件打开和关闭
file = open("/source/qitest.txt")
try:
data = file.read()
finally:
file.close()
可优化为如下优雅代码,它帮助我们处理了异常和忘记关闭文件流两种情况
代码语言:javascript复制with open("/source/qitest.txt") as file:
data = file.read()
以上内容通过串联前几篇的知识内容,实现了产品管理的增、改、删 接口,并且都是通过数据的方式存储值,下一篇讲基础后端服务接口实现其产品管理的基础交互,你将学到很多新的组件。
最后大奇再啰嗦一句给个建议,文章中给出的大量源代码,希望如果你还是处于语言学习基础阶段,实战的代码最好只是作为参考,真正的要自己一行行敲出来,不要直接拷贝,否则你不会记住多少,也不会遇到问题,更不会锻炼出解决问题的能力。学习的路上方法资料可以参考,但想要有收获是没有捷径的,一起共勉。
上篇回顾:测试需求平台9:数据持久化与PyMySQL使用
下篇预告:Arco组件学习和页面实现