python3 + Django + Mysql + Vue + Element-UI 学习笔记:从0搭建前后端分离的测试工具平台

摘要:
供组内同学,或者诉求方使用常用查询:直接查询库表,一是查询环境需要频繁切换,二是很多字段都是枚举值,需要二次查询,效率低;在这里把一些通用的查询操作固化下来,代码中将枚举值转义,易于理解常用链接地图:支持区分环境,含公司通用的研发/测试平台地址、小工具地址、FAQ等接口MOCK管理2、项目结构后端:python3+django+mysql前端:Vue+Element-UI+vue-template+Js3、放几张预览:首页及菜单测试数据构造:小工具集:常用链接:进入正题。

2020.03.23  INIT

中间换了工作,好久没写了

2021.05.08  新增:页面预览、功能实现样例(含后端接口、前端页面、配置绑定)

2021.05.13  新增:首页增加访问人数统计功能

2021.05.19  新增:用户信息查询接口、页面开发

2021.06.10  新增:生成图片功能

2021.06.18  新增:文本处理工具下载页面

2021.06.30  优化:静态图片、生成图片的备注支持中文

- - - - - - - -- - - - - - - -- - - - - - - -- - - - - - - -- - - - - - - -- - - - - - - -

前言

1、概述

此前一直想把测试工作过程中的一些通用的工具、方法、FAQ,集成为一个通用的测试工具。

也尝试过用python的Tkinter做的GUI(有兴趣的话,见: https://www.cnblogs.com/chenyuebai/p/7150382.html

虽然能用,但缺点也很明显:不美观、扩展性差、需要打包下载。

因此,想搭个Web-Browser、前后端分离的测试平台,把测试的一些能力集成上去,顺便学习下前后端开发知识。

功能范围大概包含:

(1)测试数据构造:日常测试、配合联调,需要多次重复的构造测试数据(接口、落库表、redis、收发kafka消息...);针对高频操作沉淀出TOP场景,落到测试平台中。供组内同学,或者诉求方使用

(2)常用查询:直接查询库表,一是查询环境需要频繁切换,二是很多字段都是枚举值,需要二次查询,效率低;在这里把一些通用的查询操作固化下来,代码中将枚举值转义,易于理解

(3)常用链接地图:支持区分环境,含公司通用的研发/测试平台地址、小工具地址、FAQ等

(4)接口MOCK管理

2、项目结构

(1)后端:python3 + django + mysql

(2)前端:Vue + Element-UI + vue-template + Js

3、放几张预览:

(1)首页及菜单

python3 + Django + Mysql + Vue + Element-UI 学习笔记:从0搭建前后端分离的测试工具平台第1张

(2)测试数据构造:

python3 + Django + Mysql + Vue + Element-UI 学习笔记:从0搭建前后端分离的测试工具平台第2张

(3)小工具集:

python3 + Django + Mysql + Vue + Element-UI 学习笔记:从0搭建前后端分离的测试工具平台第3张

(4)常用链接:

python3 + Django + Mysql + Vue + Element-UI 学习笔记:从0搭建前后端分离的测试工具平台第4张

进入正题。

一、环境准备

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;            #立即生效

installPathmy.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、目录说明

python3 + Django + Mysql + Vue + Element-UI 学习笔记:从0搭建前后端分离的测试工具平台第5张

4、配置数据库信息

由于选择的是mysql,需要配置数据库信息

MySite/__init__.py:

importpymysql
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请求

--> 后端MySiteurls.pyurlpatterns中寻找路由信息 -->调用views.py中的视图函数--> 视图函数调用viewImpl中具体的业务代码 --> 数据库处理 --> 组装结果 --> 返回结果至前端

--> 前端页面渲染展示

以查询某权益当前可用天数功能为例(涉及具体业务,部分代码有删改):

1、后端接口开发

(1)接口业务逻辑实现:viewImpl.py

def get*****VaildDay(self,request):
    Logger.info("request.POST = %s" %request.POST)
    #记录访问信息
utils.SysRecord().visitRecord(request)
    response ={}
    validDay = ""tableTemplate = "********"sqlTemplate = '''********'''
    #解析、校验入参
    userId = request.POST.get("userId")
    env = request.POST.get("env")
    strTools =WorkTools.StringFactory()
    if (strTools.isNull(userId) == False) or (strTools.isNull(env) ==False):
        Logger.error("校验参数失败,存在入参为空")
        response["code"] = "1"response["message"] = "校验参数失败,存在入参为空;userId、env必填"
        return JsonResponse(data=response, json_dumps_params={'ensure_ascii': False})
    #获取分表
    tableName = utils.TableEditor().getSubTableName(tableTemplate, userId, "1")
    #获取当前时间
    currentTime =WorkTools.SystemTools().get_current_time()
    #组装并执行sql
    sql = sqlTemplate.replace("tableName_template", tableName).replace("userId_template", userId).replace(
        "currentTime_template", currentTime)
    Logger.info("组装sql完成,sql = %s" %sql)
    mysqlConnectInfo = sysConfig.MYSQL["equity_%s" %env]
    mysql = WorkTools.MysqlTools(mysqlConnectInfo["host"], mysqlConnectInfo["port"], mysqlConnectInfo["db"],mysqlConnectInfo["username"], mysqlConnectInfo["password"])
    sqlConnect =mysql.connectMysqlDatabase()
    validCountDecimal = mysql.executeSqlLine(sqlConnect, sql)["data"]
    validCount =validCountDecimal[0][0]
    try:
        #
        if (validCount == None) or (validCount == "None"):
            validDay =0
        else:
            validCountStr = str(validCount.quantize(Decimal('0')))
            validDay = int(validCountStr) / 100response["code"] = "0"response["message"] = "success.查询【*****当前可用天数】成功"response["userId"] =userId
        response["validDay"] =validDay
        response["env"] =env
        Logger.info("get*****VaildDay  success.userId = %s" %userId)
    exceptException as e:
        executeResult = str(e) + "" +traceback.format_exc()
        Logger.error("计算********权益当前可用天数失败: %s详细信息请见日志" %validCountDecimal)
        response["code"] = "1"response["message"] = str(e) + "" +traceback.format_exc()
    return JsonResponse(data=response, json_dumps_params={'ensure_ascii': False})

(2)view层配置:views.py

#司机优先抢货权益-查询司机优先抢货权益当前可用天数
@csrf_exempt
def get*****VaildDay(request):
    return viewImpl.Equity().get*****VaildDay(request)

(3)url层路由配置:urls.py

urlpatterns =[
    #前端分离
    url('home', TemplateView.as_view(template_name="index.html")),
    #权益
    path("api/equity/get*****VaildDay", views.get*****VaildDay, name="get*****VaildDay"),
]

2、前端页面开发

前端页面以前用原生JS写过一些,也是面向百度编码的水平。。且写出来的页面不是很美观,不如找现成的轮子,又快又好看。

听前端同事安利了Vue+Element-UI,熟悉了两天,写写简单页面够用了

进入正题:

(1)静态页面开发:equity.vue 有删改,仅供参考

<template>
  <div class="equity-****">
        <h4>查询测试数据:****当前可用天数</h4>
        <el-form-item label="选择环境">
        <el-radio-group v-model="form.requestResource">
          <el-radio label="dev" />
          <el-radio label="qa" />
        </el-radio-group>
      </el-form-item>
      <el-form-item label="userId">
        <el-input v-model="form.requestUserId"style=" 25%;"type="number"/>
      </el-form-item>
    <el-form-item>
        <el-button type="primary"@click="get*****VaildDay()">查询</el-button>
        <el-button @click="onCancel">取消</el-button>
    </el-form-item>
    <el-form-item>
      <el-row>
        <el-table :data="validDay"style=" 40%"border>
          <el-table-column prop="userId"label="userId"min-width="60">
            <template scope="scope"> {{ scope.row.userId }} </template>
          </el-table-column>
          <el-table-column prop="validDay"label="当前可用天数"min-width="40">
            <template scope="scope"> {{ scope.row.validDay }} </template>
          </el-table-column>
        </el-table>
      </el-row>
    </el-form-item>    
  </div>
</template>

<script>import { ge*******VaildDay } from '@/api/equity'


export default{
  name: 'equity-*****',  
  data() {
    return{
      form: {
      },
      validDay:[]
    }
  },
  methods: {
    onCancel() {
      this.$message({
        message: 'cancel!',
        type: 'warning'})
    },
    ge*******VaildDay() {
      this.listLoading = true
      varparams = newURLSearchParams();//要使用这种类型的数据
params.append('userId', this.form.requestUserId);
      params.append('env', this.form.requestResource);
      this.myparms=params;
      ge*******VaildDay(this.myparms).then(response =>{
        console.log("ge*******VaildDay response = ",response) //for debug
        varmsg =response.message
        //var detail = response
        this.showSuccess("提示",msg)
        //生成数组
        varvalidDayInfo = newObject()
        validDayInfo.userId =response.userId
        validDayInfo.validDay =response.validDay
        varvalidDayInfoArray=newArray()
        validDayInfoArray[0] =validDayInfo
        this.validDay =validDayInfoArray
      })
    },
    showSuccess(title,msg) {
        this.$alert(msg, title, {
          confirmButtonText: '确定',
          callback: action =>{
            //this.$message({
            //type: 'info',
            //message: `action: ${ action }`
            //});
}
        });
      }
  }
}
</script>

<style scoped>.line{text-align:center;
}
</style>
View Code

(2)调用后端接口:equity.js

import request from '@/utils/request'import { MessageBox, Message } from 'element-ui'
export function ge*******VaildDay(data) {
  Message({
      message: "请求已发出,请稍等" || 'Info',
      type: 'Info',
      //duration: 5 * 1000
})
  returnrequest({
    url: '/equity/ge*******VaildDay',
    method: 'post',
    data
  })
}

请求是基于request.js发出的,模板中已封装好,修改一下相关的状态码校验即可,不再赘述。

前端开发完成后,cmd执行npm run build,如果代码OK,就能够看到所有的代码会被webpack自动打包到dist目录下了,至此前端开发完成;

3、前后端绑定:settings.py

(1)贴一些关键配置

TEMPLATES =[
    {
        'BACKEND': 'django.template.backends.django.DjangoTemplates',
        'DIRS': [
            # 配置Django项目的模板搜索路径
            os.path.join(BASE_DIR, r"vue-admindist"),

        ],
        '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-admindiststatic"),
]

4、启动服务、访问

(1)启动后端服务:manage.py runserver 0.0.0.0:8080

python3 + Django + Mysql + Vue + Element-UI 学习笔记:从0搭建前后端分离的测试工具平台第6张

(2)访问: http://{本地IP}:8080/home 代码没有问题的话,就可以进入首页了;

(3)找到自己所属的菜单,点击即可使用上述开发的功能了

页面:

python3 + Django + Mysql + Vue + Element-UI 学习笔记:从0搭建前后端分离的测试工具平台第7张

接口信息:

python3 + Django + Mysql + Vue + Element-UI 学习笔记:从0搭建前后端分离的测试工具平台第8张

四、功能迭代

1、首页增加访问人数统计功能

(1)表设计

CREATE TABLE`visit_record` (
  `id` int(11) NOT NULLAUTO_INCREMENT,
  `api` varchar(200) NOT NULL COMMENT '访问接口',
  `param` varchar(200) DEFAULT '' COMMENT '访问接口参数',
  `ip` varchar(100) DEFAULT 'default' COMMENT '访问者ip',
  `user_id` varchar(100) DEFAULT 'default' COMMENT '访问者id',
  `user_name` varchar(100) DEFAULT 'default' COMMENT '访问者名',
  `env` varchar(100) NOT NULL COMMENT '环境类型:dev,qa',
  `create_time` datetime NOT NULL,
  PRIMARY KEY(`id`)
) ENGINE=InnoDB AUTO_INCREMENT=1654 DEFAULT CHARSET=utf8;

(2)utils.py visitRecord实现:记录访问信息落表

classSysRecord():
    #记录系统访问次数,落库
    defvisitRecord(self,request):
        userId = "default"userName = "default"
        if notrequest.POST.urlencode():
            param = "default"
        else:
            param =request.POST.urlencode()
        if not request.POST.get("env"):
            env = "default"
        else:
            env = request.POST.get("env")
        api =request.path
        ip =DjangoTools().getRequestIp(request)
        createTime =WorkTools.SystemTools().get_current_time()
        sqlTemplate = """INSERT INTO `mysite`.`visit_record` (`api`, `param`, `ip`, `user_id`, `user_name`, `env`, `create_time`) VALUES ( 'api_template', 'param_template', 'ip_template', 'user_id_template', 'user_name_template', 'env_template', 'create_time_template');"""
        #组装并执行sql
        sql = sqlTemplate.replace("api_template",api).replace("param_template",param).replace("ip_template",ip).replace("user_id_template",userId).replace("user_name_template",userName).replace("env_template",env).replace("create_time_template",createTime)
        Logger.info("visitRecord 记录访问信息:组装sql完成,sql = %s" %sql)
        try:
            mysqlConnectInfo = sysConfig.MYSQL["mysql_own"]
            Logger.info("mysqlConnectInfo = %s" %mysqlConnectInfo)
            mysql = WorkTools.MysqlTools(mysqlConnectInfo["host"], mysqlConnectInfo["port"], mysqlConnectInfo["db"],mysqlConnectInfo["username"], mysqlConnectInfo["password"])
            sqlConnect =mysql.connectMysqlDatabase()
            mysql.executeSqlLine(sqlConnect, sql)
        exceptException as e:
            ret = str(e) + "" +traceback.format_exc()
            Logger.error("visitRecord 记录访问信息失败,api=%s,param=%s,env=%s,ip=%s,userId=%s,userName=%s,createTime=%s" % (api, param, env, ip, userId, userName,createTime))

(3)页面&接口入口处增加统计:

viewImpl.py中,对页面或对外提供的接口,调用visitRecord()记录访问信息,如在查询用户信息接口中增加:

classUsers():
    defqueryUserInfo(self,request):
        #记录访问信息
utils.SysRecord().visitRecord(request)
        #以下省略

(4)后端提供访问次数查询接口

viewImpl.py

classDashboard():
    defqueryTotalVisitNum(self,request):#记录访问信息
utils.SysRecord().visitRecord(request)
        #
        response ={}
        #组装并执行sql
        sql = '''select count(*) from visit_record;'''mysqlConnectInfo = sysConfig.MYSQL["mysql_own"]
        mysql = WorkTools.MysqlTools(mysqlConnectInfo["host"], mysqlConnectInfo["port"], mysqlConnectInfo["db"],mysqlConnectInfo["username"], mysqlConnectInfo["password"])
        sqlConnect =mysql.connectMysqlDatabase()
        totalVisitNum = mysql.executeSqlLine(sqlConnect, sql)["data"][0][0]
        try:
            #组装结果返回
            response["code"] = "0"response["message"] = "success.查询累计访问量完成"response["totalVisitNum"] =str(totalVisitNum)
            Logger.info("queryUserInfo success.id = %s" %id)
        exceptException as e:
            executeResult = str(e) + "" +traceback.format_exc()
            Logger.error("queryUserInfo:查查询累计访问量失败:%s"%executeResult)
            response["code"] = "1"response["message"] =executeResult
        return JsonResponse(data=response, json_dumps_params={'ensure_ascii': False})

views.py

#首页dashboard#查询累计访问量
@csrf_exempt
defqueryTotalVisitNum(request):
    return viewImpl.Dashboard().queryTotalVisitNum(request)

urls.py

#首页dashboard
path("api/dashboard/queryTotalVisitNum", views.queryTotalVisitNum, name="queryTotalVisitNum"),

(3)前端页面开发

dashboard.js 中配置接口

import request from '@/utils/request'import { MessageBox, Message } from 'element-ui'
export functionqueryTotalVisitNum(data) {
    returnrequest({
      url: '/dashboard/queryTotalVisitNum',
      method: 'post',
      data
    })
  } 

dashboardinedx.vue stye太多不贴了

<template>
<el-row :gutter="40"class="panel-group">
    <div class="dashboard-container">
    <div class="dashboard-text">******测试</div>
    <div class="dashboard-text">你好: {{ name }}</div>
  </div>
    <el-col :xs="12":sm="12":lg="6"class="card-panel-col">
      <div class="card-panel">
        <div class="card-panel-icon-wrapper icon-people">
          <svg-icon icon-class="user"class-name="card-panel-icon" />
        </div>
        <div class="card-panel-description">
          <div class="card-panel-text">累计访问
          </div>
    <div class="dashboard-text">{{ totalVisitNum }}</div>
        </div>
      </div>
    </el-col>
  </el-row>
</template>

<script>import { mapGetters } from 'vuex'import { queryTotalVisitNum } from '@/api/dashboard'
export default{
  name: 'Dashboard',
  computed: {
    ...mapGetters([
      'name'])
  },
  data() {
    return{
      totalVisitNum:this.queryTotalVisitNum()
    }
  },
  methods: {
    queryTotalVisitNum() {
      this.listLoading = truequeryTotalVisitNum().then(response =>{
        console.log("queryTotalVisitNum response = ",response) //for debug
        varmsg =response.message
        vartotalVisitNum =response.totalVisitNum
        this.totalVisitNum =totalVisitNum
      })
    }
  }
}

</script>

<style lang="scss"scoped></style>

OK,至此就完成了,看下效果:

python3 + Django + Mysql + Vue + Element-UI 学习笔记:从0搭建前后端分离的测试工具平台第9张

免责声明:文章转载自《python3 + Django + Mysql + Vue + Element-UI 学习笔记:从0搭建前后端分离的测试工具平台》仅用于学习参考。如对内容有疑问,请及时联系本站处理。

上篇【转载】 java根据ip地址获取详细地域信息Swift-数组下篇

宿迁高防,2C2G15M,22元/月;香港BGP,2C5G5M,25元/月 雨云优惠码:MjYwNzM=

相关文章

使用http协议下载网络图片

     出于公司的一个需求,我要写一个下载网易微博的一个头像,但是网易微博的的图片是不允许外链的,所以我们只下载它,好了动手了,呵呵     一开始我用这样来读流   using (var stream = response.GetResponseStream())            {                //byte[] bytes...

mysql(2)-启动mysql报错The server quit without updating PID file!

mysql启动报错 今天启动mysql又一次报错:The server quit without updating PID file!记得上次出现这个问题的时候,尝试了一些常规的方法,未果,所以索性重新进行安装。但是,相同的问题今天又出现了!!!OH, my god!恰巧今天时间充裕,尝试各种办法,终于皇天不负有心人,经过一个小时的奋战后,终于让我给搞...

【精彩回顾】第二届微医前端技术沙龙(附PPT下载)

5 月 25 日,以「无界」为主题的第二届微医前端技术沙龙成功举办。本届沙龙的演讲题目涵盖了前端技术几个主要的应用场景,包括服务端、桌面端以及跨平台的开发。最近几年前端技术发展非常快,各种可以提高开发效率的新技术和框架层出不穷,让原来的前端工程师蜕变成了新一代的全端工程师,技术之间的界限被打破,相互碰撞、融合和进化。 活动伊始,本届沙龙活动主持人,来自...

聊聊 webpack 打包如何压缩包文件大小

想必很多人都经历过做完一个项目后,再打包发现某些文件非常大,导致页面加载时很慢,这就很影响用户体验了,所以在我经历了一些打包后,讲讲如何有效地缩小包体积,加快页面的首屏渲染 动态 polyfill 相信很多项目都会用到polyfill 那么一整个polyfill 会占据很多的空间,这个时候需要使用动态polyfill来解决这个问题了: 在 index.ht...

vue 日期转换

y: date.getFullYear(), 如果报错 time 要new Data(time) <!DOCTYPE html> <html> <head lang="en"> <meta charset="UTF-8"> <title></title> <!-...

MySQL---&amp;gt;数据库的简介和安装

1.什么是MySQL: MySQL是一个小型关系型数据库管理系统,开发者伟瑞典MySQL AB公司。 目前MySQL被广泛的应用在Internet上的中小型网站中。由于体积小,速度快,总体拥有成本低,尤其是开放源码这一特点能够降低往网站总体拥有成本而选择了MySQL作为数据库。 2.市面上的数据库: Oracle 应用广泛功能强大分布式数据库系统...