python3 + Django + Mysql + Vue + Element-UI 学习笔记:从0搭建前后端分离的测试工具平台
2021.06.10 INIT
2021.06.24 增加生成图片支持备注文字功能;redis链接方式切换为cluster
2021.06.29 生成图片功能支持中文备注
2021.07.01 修复通过域名访问静态文件类型错误;生成图片增加尺寸信息
2021.07.09 域名接入完成;增加mysqldb记录访问信息;系统信息页面增加访问信息统计功能
2021.07.30 增加支持按掩码查询用户信息
2021.08.05 增加支持发送mq消息功能;mq搜索框支持模糊搜索;查询用户信息增加手机号回显处理
2021.08.11 查询用户信息反查展示真实手机号(掩码反查)
2021.08.12 增加支持优惠券一键发放、作废功能
2021.08.26 增加支持广告位一键同步;发送mq页面;优惠券使用期限调整功能
2021.09.11 增加用户权益查询功能
2021.09.15 新增定时任务功能
2021.09.16 生成图片支持gif格式
2021.09.23 新增留言板,支持留言、回复、分页展示
2021-09-26 生成图片支持自定义背景色、文字颜色
2021-09-29 增加支持权益一键发放功能;优化优惠券发放增加校验
2021-10-29 增加定时任务开启时不可修改的校验;首次开启时,自动执行一次;造数增加积分操作入口
2021-11-08 增加查询用户优惠券列表功能
2021-12-03 增加用户积分查询与修改功能
2021-12-25 节点达标增加手动触发说明、支持用户自行新增节点、编辑节点
2022-01-21 新增业务域管理模块:支持用户自行维护;支持管理员审批。计划从这次开始,新功能写开发记录:https://www.cnblogs.com/chenyuebai/p/15830681.html
前言
1、概述
此前一直想把测试工作过程中的一些通用的工具、方法、FAQ,集成为一个通用的测试工具。
也尝试过用python的Tkinter做的GUI(有兴趣的话,见: https://www.cnblogs.com/chenyuebai/p/7150382.html )
虽然能用,但缺点也很明显:不美观、扩展性差、需要打包下载。
因此,想搭个Web-Browser、前后端分离的测试平台,把测试的一些能力集成上去,顺便学习下前后端开发知识。
功能范围大概包含:
(1)测试数据构造:日常测试、配合联调,需要多次重复的构造测试数据;针对高频操作沉淀出TOP场景,落到测试平台中。供组内同学,或者诉求方使用
(2)常用查询:直接查询库表,一是查询环境需要频繁切换,二是很多字段都是枚举值,需要二次查询字段含义,效率低;在这里把一些通用的查询操作固化下来,代码中将枚举值转义,易于理解
(3)常用链接地图:支持区分环境,含公司通用的研发/测试平台地址、小工具地址、FAQ等
(4)定时任务:支持MYSQL、HTTP、RPC定时执行
(5)MQ MOCK仓库:
2、项目结构
(1)后端:python3 + django + mysql
(2)前端:Vue + Element-UI + Js
3、功能预览:
(1)首页及菜单、系统访问地图:
(2)测试数据构造
(3)MQ MOCK仓库:
(4)定时任务
(5)留言板
一、环境准备
1、Python 3.6.1
2、Django 2.2.6 pip安装即可
3、mysql 5.7.1 + Connector/J 5.1.48
(1)安装:mysql安装时需记录用户名、密码
(2)配置时间、字符集:
sql cmd: set global time_zone = '+8:00'; #修改mysql全局时区为北京时间,即我们所在的东8区 set time_zone = '+8:00'; #修改当前会话时区 flush privileges; #立即生效
installPath\my.ini:
在[client]节点下添加 default-character-set=utf8
在[mysqld]节点下添加 (注:collation是排序方式) character-set-server=utf8 collation-server=utf8_general_ci
restart
SHOW VARIABLES LIKE 'char%';
(3)本地启动mysql服务:
CMD: net start mysql_service_ch
(4)数据库迁移
manage.py makemigrations
manage.py migrate
(5)创建超级用户
manage.py createsuperuser
(6)设置后台管理界面为中文
setting.py中: LANGUAGE_CODE = 'zh-hans' TIME_ZONE = 'Asia/Shanghai'
前端开发环境搭建,参考:
https://www.cnblogs.com/goldlong/p/8027997.html
二、创建项目、应用
1、创建项目
django-admin.py startproject MySite运行服务:manage.py runserver 本地访问:http://127.0.0.1:8000/
ps:若希望外部机器(同一网络内)访问本机的Django服务 可:manage.py runserver 0.0.0.0:8080
并将setting.py中,ALLOWED_HOSTS = ['*', ]
2、创建应用
manage.py startapp Post
3、目录说明
4、配置数据库信息
由于选择的是mysql,需要配置数据库信息
MySite/__init__.py:
import pymysql
pymysql.install_as_MySQLdb()
MySite/setting.py:
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.mysql',
'NAME': 'mysite',
'USER':'root',
"PASSWORD":"passwd123456",
"HOST":"127.0.0.1",
"PORT":"3306"
}
}
至此,环境基本搭建完成;
三、功能实现
大概思路
--> 前端浏览器访问/点击,触发http请求
--> 后端 MySite\urls.py\urlpatterns中寻找路由信息 --> 调用views.py中的视图函数-->处理http请求、参数处理、封装返回 --> 视图函数调用viewImpl中具体的业务代码 --> 数据库处理 --> 组装结果 --> 返回结果至前端
--> 前端页面渲染展示
以查询留言板列表数据的功能为例(部分代码有删改):
1、后端接口开发
(1)接口业务逻辑实现:viewImpl.py
def queryMsgBoardList(self,msgId,msgType,msgAuthor,pageNb,pageSize): msgBoardList = [] totalCount = None # 处理分页 startIndex = (pageNb-1) * pageSize # 组装查询条件 sqlTemplate = '''select **** from test_**** where trigger_type='MSG_BOARD' {} ORDER BY create_time desc limit {},{};''' msgIdCondition = "" if msgId and msgId != 'undefined': msgIdCondition = " and id='{}'".format(msgId) msgTypeCondition = "" if msgType and msgType != 'undefined': msgTypeCondition = " and detail_info like '%{}%'".format(msgType) msgAuthorCondition = "" if msgAuthor and msgAuthor != 'undefined': msgAuthorCondition = " and detail_info like '%{}%'".format(msgAuthor) sqlCondition = msgIdCondition + msgTypeCondition + msgAuthorCondition querySql = sqlTemplate.format(sqlCondition,startIndex,pageSize) try: # 查询留言列表 sqlRetData = utils.MysqlUtils().executeSql(querySql,"****db_dev") No = 1 for item in sqlRetData: detailInfo = json.loads(item[4].replace('\n', '\\r\\n')) msgBoard = {"msgId": item[0], "msgType": detailInfo["msgType"],"msgBody": detailInfo["msgBody"],"msgAuthor": detailInfo["msgAuthor"],"createTime": str(item[6]).replace("T", " ")} No = No + 1 msgBoardList.append(msgBoard) # 查询总数量(sql中不带入分页条件) queryCtSql = "select count(*) from test_**** where trigger_type='MSG_BOARD' {};".format(sqlCondition) totalCount = int(utils.MysqlUtils().executeSql(queryCtSql,"coupondb_dev")[0][0]) Logger.info("queryMsgBoardList success. msgBoardList = {},totalCount={}".format(msgBoardList,totalCount)) except Exception as e: executeResult = str(e) + "\n" + traceback.format_exc() Logger.error("queryMsgBoardList 异常:%s" % executeResult) finally: return msgBoardList,totalCount
(2)view层:views.py
def queryMsgBoardList(request): # 入参打印 utils.SysRecord().LoggerInfoBatch(["request = {}".format(request), "request.body = {}".format(request.body),"request.POST = {}".format(request.POST)]) # 记录访问信息 utils.SysRecord().visitRecord(request) # 定义参数,获取入参 msgId = request.POST.get("msgId") msgType = request.POST.get("msgType") msgAuthor = request.POST.get("msgAuthor") pageNb = request.POST.get("pageNb") pageSize = request.POST.get("pageSize") response = {} # 校验入参 if not WorkTools.StringFactory().batchCheckParams([pageNb,pageSize]): Logger.error("Post.views.queryMsgBoardList() 校验参数失败,存在入参为空,请检查") return utils.DjangoTools().returnResponse({"code": "1", "message": "queryMsgBoardList() 校验参数失败,存在入参为空,请检查"}) try: # 查询留言板数据 pageNb = int(pageNb) pageSize = int(pageSize) msgBoardList,totalCount = viewImpl.MsgBoard().queryMsgBoardList(msgId,msgType,msgAuthor,pageNb,pageSize) # 组装结果返回 response["code"] = "0" response["message"] = "查询成功" response["msgBoardList"] = msgBoardList response["totalCt"] = totalCount Logger.info("queryMsgBoardList():查询成功") except Exception as e: executeResult = str(e) + "\n" + traceback.format_exc() Logger.error("queryMsgBoardList():查询异常:%s" % executeResult) response["code"] = "1" response["message"] = executeResult return JsonResponse(data=response, json_dumps_params={'ensure_ascii': False})
(3)url层路由配置:urls.py
path("api/msgBoard/queryMsgBoardList", views.queryMsgBoardList, name="queryMsgBoardList"),
2、前端页面开发
前端页面以前用原生JS写过一些,也是面向百度编码的水平。且写出来的页面不是很美观,不如找现成的轮子,又快又好看。
听前端的同事安利了Vue+Element-UI,熟悉了两天,写写简单页面够用了
进入正题:
(1)静态页面开发:msgBoard.vue 列表展示部分:
<div> <el-form> <el-form-item> <el-row> <el-table :data="msgBoardList" row-class-name="mqRowClass" class="msgBoardListClass"> <!-- 折叠 --> <el-table-column type="expand"> <template slot-scope="props"> <el-form label-position="left" inline class="fold-table-expand"> <!-- <el-form-item label="序号"> <span>{{ props.row.No }}</span> </el-form-item> <el-form-item label="留言类型"> <span>{{ props.row.msgType }}</span> </el-form-item> --> <el-form-item label="楼层"> <span>{{ props.row.msgBody }}</span> </el-form-item> <!-- <el-form-item label="提交人"> <span>{{ props.row.msgAuthor }}</span> </el-form-item> <el-form-item label="留言时间"> <span>{{ props.row.createTime }}</span> </el-form-item> --> </el-form> </template> </el-table-column> <!-- 展示列表 --> <el-table-column label="ID" min-width="10" align="center"> <template scope="scope"> {{ scope.row.msgId }} </template> </el-table-column> <el-table-column label="留言类型" min-width="18" align="center"> <template scope="scope"> {{ scope.row.msgType }} </template> </el-table-column> <el-table-column label="留言内容" min-width="125" align="center" show-overflow-tooltip="true" class="cellClass"> <template scope="scope"> {{ scope.row.msgBody }} </template> </el-table-column> <el-table-column label="提交人" min-width="15" align="center"> <template scope="scope"> {{ scope.row.msgAuthor }} </template> </el-table-column> <el-table-column label="留言时间" min-width="22" align="center"> <template scope="scope"> {{ scope.row.createTime }} </template> </el-table-column> <!-- <el-table-column label="备注" min-width="18" align="center"> <template scope="scope"> {{ scope.row.msgComment }} </template> </el-table-column> --> <el-table-column label="操作" min-width="20" align="center"> <template slot-scope="scope"> <el-button type="text" @click="replyMsg(scope.row)">回复</el-button> <!-- <el-button type="text" @click="editCrontabTask(scope.row)">批注 - 开发中</el-button> --> </template> </el-table-column> </el-table> </el-row> </el-form-item> </el-form> </div>
(2)调用后端接口:msgBoard.js
import request from '@/utils/request' import { MessageBox, Message } from 'element-ui' export function queryMsgBoardList(data) { Message({ message: "请求已发出,请稍等" || 'Info', type: 'Info', // duration: 5 * 1000 }) return request({ url: '/msgBoard/queryMsgBoardList', method: 'post', data }) }
请求是基于request.js发出的,模板中已封装好,修改一下相关的状态码校验即可,不再赘述。
前端开发完成后,cmd敲编译命令,如果代码OK,就能够看到所代码会被webpack自动打包到dist目录下了,至此前端开发完成;
3、前后端绑定:settings.py
(1)贴一些关键配置
TEMPLATES = [ { 'BACKEND': 'django.template.backends.django.DjangoTemplates', 'DIRS': [ # 配置Django项目的模板搜索路径 os.path.join(BASE_DIR, r"vue-admin\dist"), ], 'APP_DIRS': True, 'OPTIONS': { 'context_processors': [ 'django.template.context_processors.debug', 'django.template.context_processors.request', 'django.contrib.auth.context_processors.auth', 'django.contrib.messages.context_processors.messages', ], }, }, ] # Add for vuejs 配置一下静态文件的搜索路径 STATICFILES_DIRS = [ os.path.join(BASE_DIR, r"vue-admin\dist\static"), ]
4、启动服务、访问
(1)启动后端服务:manage.py runserver 0.0.0.0:8080
(2)访问: http://{本地IP}:8080/home 代码没有问题的话,就可以进入首页了;
(3)找到自己所属的菜单,点击即可使用上述开发的功能了