项目地址:

​GitHub - 18713341733/djangoplatform​

背景:

测试的时候,有一个清理数据的场景,需要将一些信息从不同的几个库里的表删掉。

比如删掉A信息,就需要去各种myslq、oracle、redis的库,把这些数据删掉。

人工删除,比较麻烦,要连接各种库,找各种表,然后还有可能删错数据。

所以做了一个简易平台。

前端输入手机号。后端做sql删除。

实现的功能:

前端就是一个文本输入框,输入手机号,支持一个或者多个手机号输入。多个手机号用逗号隔开

后端就是根据这手机号去做sql删除。

一、搭建django自动化平台(实现一键执行sql)_sql

一、环境搭建

1、使用的django==2.1.10版本

2、使用pycharm建的django项目

3、使用的是虚拟环境。

/用户/zhaohui/PycharmProjects/

我想在PycharmProjects 这个文件夹下,新建一个名为myplatform的 Django项目。

1、先安装django==2.1.10

2、使用pycharm打开PycharmProjects文件夹

3、在pycharm的Terminal 下执行以下命令,来创建myplatform项目。

django-admin startproject myplatform

一、搭建django自动化平台(实现一键执行sql)_运维_02

4、 myplatform项目已经创建成功了。使用pycharm重新打开myplatform项目。

一、搭建django自动化平台(实现一键执行sql)_html_03

这里对结构简单进行一下解释:

testplatform/__init__.py:一个空的文件,用它标识一个目录为 Python 的标准包。

testplatform/settings.py:Django 项目的配置文件,包括 Django 模块应用配置,数据库配置,模板配置等。

testplatform/urls.py:Django 项目的 URL 声明。

testplatform/wsgi.py:为 WSGI 兼容的 Web 服务器服务项目的切入点。 manage.py:一个命令行工具,可以让你在使用 Django 项目时以不同的方式进行交互。
 

5、创建应用。在当前目录(myplatform)下,创建一个名为sign的APP。

在Terminal 下执行:

python3 manage.py startapp sign

一、搭建django自动化平台(实现一键执行sql)_django_04

 创建完成后:

一、搭建django自动化平台(实现一键执行sql)_django_05

创建应用完成后,将该应用加入项目,修改settings.py文件。 

一、搭建django自动化平台(实现一键执行sql)_html_06

migrations/:用于记录 models 中数据的变更。

admin.py:映射 models 中的数据到 Django 自带的 admin 后台。

apps.py:在新的 Django 版本中新增,用于应用程序的配置。

models.py:创建应用程序数据表模型(对应数据库的相关操作)。

tests.py:创建 Django 测试。

views.py:控制向前端显示哪些数据。
 

6、运行项目

在Terminal下执行

python3 manage.py runserver 127.0.0.1:8001

一、搭建django自动化平台(实现一键执行sql)_自动化_07

二、配置myplatform项目 虚拟环境。

教程:

​Pycharm创建虚拟环境_傲娇的喵酱的博客pycharm虚拟环境有什么用​

里面的第三章:

 ​​三、已有项目,创建并使用一个新的虚拟环境​

配置完虚拟环境就是要装依赖包了。我的项目里整理出了requirements.txt文件。

在当前环境,导入依赖包

python3 -m pip install -r requirements.txt

三、开始写项目

我新建的项目,项目名称为:djangoplatform

应用app名称为:deleteSqlApp

3.1 创建首页home.html

在应用deleteSqlApp下创建模板文件夹(templates)

在templates文件夹下创建首页页面Home.html

一、搭建django自动化平台(实现一键执行sql)_html_08

Home.html

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>平台首页</title>
</head>

<body bgcolor="#f5f5dc">
<h1 style="text-align: center;">自动化系统首页</h1>



<div style='text-align: center'>

</div>
<div style="text-align: center;margin-top: 30px">
<input type="submit" name="Submit" value="删除注册信息" onclick=window.open("/deleteSql/") style="font-size:20px;width: 400px; height: 60px;cursor:pointer">
</div>
<div style="text-align: center;margin-top: 30px">
<input type="submit" name="Submit" value="删除form" onclick=window.open("/deleteForm/") style="font-size:20px;width: 400px; height: 60px;cursor:pointer">
</div>
<div style="text-align: center;margin-top: 30px">
<input type="submit" name="Submit" value="扩展2" onclick=window.open("/cc/index/") style="font-size:20px;width: 400px; height: 60px;cursor:pointer">
</div>



</body>
</html>

一、搭建django自动化平台(实现一键执行sql)_运维_09

 就是实现一个页面的跳转。

首页三个点击按钮,我们拿第一个举例。

<div style="text-align: center;margin-top: 30px">
<input type="submit" name="Submit" value="删除注册信息" onclick=window.open("/deleteSql/") style="font-size:20px;width: 400px; height: 60px;cursor:pointer">
</div>


οnclick=window.open("/deleteSql/")


当点击【删除注册信息】这个按钮时,会去调用urls.py里面的deleteSql路由。

3.2 创建手机号输入页面

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>删除注册信息</title>
</head>

<body bgcolor="#f5f5dc">
<h1 style="text-align: center;">删除注册信息</h1>



<form action="/inputIphoneNumber/" method="post">
<!-- 文本域-->
<div style="text-align: center;margin-top: 30px">
<p>
<textarea name="textarea" cols="50" rows="10" placeholder="输入多个手机号,逗号隔开"></textarea>
</p>
<p>
<input type="submit">
</p>
执行结果:{{ result }}<br>
输入内容:{{ content }}<br>
消息:{{ mymessages }}<br>


</div>
</form>




</body>
</html>

一、搭建django自动化平台(实现一键执行sql)_自动化_10

1、输入框使用的是form表单。当点击提交时,会去调用urls.py里面的inputIphoneNumber路由。

<form action="/inputIphoneNumber/" method="post">

2、模版变量的显示

    执行结果:{{ result }}<br>
输入内容:{{ content }}<br>
消息:{{ mymessages }}<br>

在这个页面,有这三个变量。将来会显示在这个html上。

3.3 配置urls.py路由

上面几个html页面,点击会触发路由。配置djangoplatform的urls.py

一、搭建django自动化平台(实现一键执行sql)_sql_11

from django.contrib import admin
from django.urls import path
from deleteSqlApp import views

from django.conf.urls import url

urlpatterns = [
url('admin/', admin.site.urls),
url(r'^home/$',views.home), # 添加home/配置路径
url(r'^$',views.home), # 添加home/配置路径
url(r'^deleteSql/$',views.deleteSql),
url(r'^inputIphoneNumber/$',views.inputIphoneNumber),
url(r'^deleteForm/$',views.deleteForm),


]

handler404=views.page_not_found
    url(r'^home/$',views.home), # 添加home/配置路径
url(r'^deleteSql/$',views.deleteSql),
url(r'^inputIphoneNumber/$',views.inputIphoneNumber),

前端触发home路由时,会调用views.home函数。

前端触发deleteSql路由时,会调用views.deleteSql函数。(这个就是点击跳转删除详情页的路由)

前端触发inputIphoneNumber路由时,会调用views.inputIphoneNumber函数。(这个就是提交手机号,进行删除的路由。)

下一步就是完善views里面的home、deleteSql、inputIphoneNumber方法。

3.4 views视图函数

# -*- coding: UTF-8 -*-
import simplejson as simplejson
from django.shortcuts import render, redirect

# Create your views here.

from django.shortcuts import render
from django.http import HttpResponse, HttpResponseRedirect
from django.views.decorators.csrf import csrf_exempt
from django.shortcuts import render_to_response
from deleteSqlApp.utils.CheckPhoneNumber import CheckPhoneNumber




def home(request):
return render(request,"Home.html")


def deleteSql(request):
return render(request,"deleteSql.html")

def deleteForm(request):
return render(request,"deleteSql.html")

def inputIphoneNumber(request):
# 前端输入数据

try:
phone_numbers = request.POST['textarea']
except:
return render(request, "deleteSql.html")

print("phone_numbers:"+phone_numbers)

# TODO: 输入手机号码的校验
result,messages = CheckPhoneNumber.check_phone_number(phone_numbers)
print("result:"+result)
if result == "False":
return render(request, "deleteSql.html", context={
"result": "失败",
"content": phone_numbers,
})
liststr = "".join(messages)
messages = "成功处理以下数据:"+liststr
return render(request, "deleteSql.html", context={
"result": "成功",
"content": phone_numbers,
"mymessages":messages,
})











#404页面
@csrf_exempt
def page_not_found(request):
return render_to_response('404.html')

home 函数,就返回home.html就可以了。

def inputIphoneNumber(request): 方法,就是处理手机号的函数。

1、获取用户输入内容。输入内容为空则直接返回我们的这个页面

2、输入内容不为空,需要校验手机号。

3、处理手机号的校验

4、拿着这些手机号去执行sql。(这里我没具体写sql的方法)

5、返回值,包含html页面,和需要展示的变量。

def inputIphoneNumber(request):
# 前端输入数据

try:
phone_numbers = request.POST['textarea']
except:
return render(request, "deleteSql.html")

print("phone_numbers:"+phone_numbers)

# TODO: 输入手机号码的校验
result,messages = CheckPhoneNumber.check_phone_number(phone_numbers)
print("result:"+result)
if result == "False":
return render(request, "deleteSql.html", context={
"result": "失败",
"content": phone_numbers,
})
liststr = "".join(messages)
messages = "成功处理以下数据:"+liststr
return render(request, "deleteSql.html", context={
"result": "成功",
"content": phone_numbers,
"mymessages":messages,
})

整体前端架构,就这样搭建完了。现在搭建后端的一些内容。

四、对输入手机号的检查。CheckPhoneNumber

一、搭建django自动化平台(实现一键执行sql)_运维_12

CheckPhoneNumber.py

# -*- coding:utf-8 -*-
# @Author: 喵酱
# @time: 2022 - 06 -10
# @File: CheckPhoneNumber.py

import re
class CheckPhoneNumber:


# 正则匹配手机号
@staticmethod
def check_phone_number(account:str):
if len(account)<11:
return "False" ,"请检查手机号格式"

number = re.findall('(13\d{9}|14[5|7]\d{8}|15\d{9}|166{\d{8}|17[3|6|7]{\d{8}|18\d{9})', account)
if number ==[]:
return "False", "请检查手机号格式"
else:
return "True", number

这里用的正则提取的手机号。

五、mysql数据相关的封装。

我的项目需要执行三个不同的mysql 数据库 A B C。

5.1 用yml文件来存储数据库连接信息。

一、搭建django自动化平台(实现一键执行sql)_html_13

格式内容:

DB:
host: 11.11.11.11
username: zhanghao
password: mima
database: mysqla
databasetype: mysql
port: 3306
note: mysql数据库A

三个数据库,每个数据库都有一个yml文件来储存,一共三个yml文件。

储存信息包括常规的ip、账号、密码、端口、数据库名。我这里还加了数据库类型,与note备注信息。这两个大家可以不写。因为我将来要扩展其他类型的数据库,所以加了这2个字段。

5.2 读取数据库配置文件的方法,YmlUtil

一、搭建django自动化平台(实现一键执行sql)_sql_14

 YmlUtil.py

import os
import yaml
from deleteSqlApp.model.DbInfo import *
from pydantic import BaseModel
class YmlUtil():

# path = os.path.abspath()

@staticmethod
def readDbYml(filePath:str) -> DbInfo:
f = open(filePath, 'r', encoding='utf-8')
cont = f.read()
x = yaml.load(cont,Loader=yaml.FullLoader)
print(x['DB'])
print(x['DB']['host'])
dbInfo=DbInfo(host=x['DB']['host'],
username=x['DB']['username'],
password=x['DB']['password'],
database=x['DB']['database'],
port=x['DB']['port']
)
# 设置类属性——setattr(object,key,value)
# 类似于建造者模式
setattr(dbInfo,"note",x['DB']['note'])

return dbInfo




if __name__ == '__main__':
db=YmlUtil.readDbYml("/Users/zhaohui/PycharmProjects/KunYuan/KunYuanJiJin/deleteSqlApp/conf/dbinfo/KD_SALE_DX.yml")
print("端口号:"+str(db.port))
print(db.port)


单独写这么一个读取数据库yml配置文件的方法,主要是用一个类来储存、传递数据库配置信息。

读取文件,return出一个 自定义数据库连接类DbInfo。

一、搭建django自动化平台(实现一键执行sql)_html_15

 DbInfo.py

from pydantic import BaseModel

class DbInfo():

# 在构造器里直接赋值,好像就不要写属性了。省了get和set方法了?
def __init__(self,host=None,username=None,password=None,database=None,databasetype=None,port=None,note=None,charset="utf8"):
self.host: str = host
self.username: str= username
self.password: str = password
self.database: str = database
self.databasetype: str = databasetype
self.port: str = port
self.note:str = note
self.charset:str = charset

用来封装储存传递数据库连接信息。

5.3 数据库连接池的封装

DBPoolSet.py

import pymysql
class DBPoolSet:
"""
数据库连接池设置
"""
# 属性

# 数据库连接编码
DB_CHARSET = "utf8"

# mincached : 启动时开启的闲置连接数量(缺省值 0 开始时不创建连接)
DB_MIN_CACHED = 1

# maxcached : 连接池中允许的闲置的最多连接数量(缺省值 0 代表不闲置连接池大小)
DB_MAX_CACHED = 3

# maxshared : 共享连接数允许的最大数量(缺省值 0 代表所有连接都是专用的)如果达到了最大数量,被请求为共享的连接将会被共享使用
DB_MAX_SHARED = 2

# maxconnecyions : 创建连接池的最大数量(缺省值 0 代表不限制)
DB_MAX_CONNECYIONS = 20

# blocking : 设置在连接池达到最大数量时的行为(缺省值 0 或 False 代表返回一个错误<toMany......> 其他代表阻塞直到连接数减少,连接被分配)
DB_BLOCKING = True

# maxusage : 单个连接的最大允许复用次数(缺省值 0 或 False 代表不限制的复用).当达到最大数时,连接会自动重新连接(关闭和重新打开)
DB_MAX_USAGE = 0

# setsession : 一个可选的SQL命令列表用于准备每个会话,如["set datestyle to german", ...]
DB_SET_SESSION = None

# creator : 使用连接数据库的模块
DB_CREATOR = pymysql

DBPoolSet 用来储存数据库连接池的一些信息。

因为我们这里要多次执行sql,如果反复连接断开数据库的话,是非常消耗性能的。所以这里采用了数据库连接池的方法。

我的项目里一个是三个数据库,这里只拿一个数据库给大家讲解。

A数据库连接池的封装。MySqlAConnectionPool

MySqlAConnectionPool.py

from dbutils.pooled_db import PooledDB
from deleteSqlApp.model.DBPoolSet import DBPoolSet
from deleteSqlApp.model.DbInfo import DbInfo

from deleteSqlApp.utils.YmlUtil import YmlUtil

"""
@功能:创建数据库连接池
"""


class MySqlAConnectionPool(object):
__pool = None
path = "/Users/PycharmProjects/djangoplatform/deleteSqlApp/conf/dbinfo/mysqla.yml"
dbInfo = YmlUtil.readDbYml(path)
dbPoolSet = DBPoolSet()

# 构造器,传DbInfo、DBPoolSet
# def __init__(self):
# self.conn = self.__getconn()
# self.cursor = self.conn.cursor()

# 创建数据库连接conn和游标cursor
def __enter__(self):
self.conn = self.__getconn()
self.cursor = self.conn.cursor()

# 创建数据库连接池,私有方法
def __getconn(self):

if self.__pool is None:
self.__pool = PooledDB(
creator=self.dbPoolSet.DB_CREATOR, # creator : 使用连接数据库的模块
mincached=self.dbPoolSet.DB_MIN_CACHED, # mincached : 启动时开启的闲置连接数量(缺省值 0 开始时不创建连接)
maxcached=self.dbPoolSet.DB_MAX_CACHED,
maxshared=self.dbPoolSet.DB_MAX_SHARED,
maxconnections=self.dbPoolSet.DB_MAX_CONNECYIONS,
blocking=self.dbPoolSet.DB_BLOCKING,
maxusage=self.dbPoolSet.DB_MAX_USAGE,
setsession=self.dbPoolSet.DB_SET_SESSION,
host=self.dbInfo.host,
port=self.dbInfo.port,
user=self.dbInfo.username,
passwd=self.dbInfo.password,
db=self.dbInfo.database,
use_unicode=False,
charset=self.dbInfo.charset
)

return self.__pool.connection()

# 释放连接池资源
def __exit__(self, exc_type, exc_val, exc_tb):
self.cursor.close()
self.conn.close()

# 关闭连接归还给链接池
# def close(self):
# self.cursor.close()
# self.conn.close()

# 从连接池中取出一个连接
def getconn(self):
conn = self.__getconn()
cursor = conn.cursor()
return cursor, conn

1、读取数据库配置文件,我这里用了绝对位置,大家自己写的时候,不要像我这么写。

5.4 得到数据库连接池的方法。

数据库连接池,不能反复创建。创建一次就够了。

这里写一个得到数据库的连接池方法,使用了单例模式。

一、搭建django自动化平台(实现一键执行sql)_sql_16

 单例模式的注解,singleton.py

# -*- coding:utf-8 -*-
# @Author: 喵酱
# @time: 2022 - 06 -10
# @File: singleton.py
#coding:utf-8
#单例模式函数,用来修饰类
def singleton(cls,*args,**kw):
instances = {}
def _singleton():
if cls not in instances:
instances[cls] = cls(*args,**kw)
return instances[cls]
return _singleton

得到连接池的方法,单例模式GetMySqlAConnection.py

一、搭建django自动化平台(实现一键执行sql)_django_17

# -*- coding:utf-8 -*-
# @Author: 喵酱
# @time: 2022 - 06 -10
# @File: GetMySqlAConnection.py
from deleteSqlApp.conf.dbConnectPool.MySqlAConnectionPool import MySqlAConnectionPool
from deleteSqlApp.utils.singleton import singleton


@singleton
class GetMySqlAConnection:
def __init__(self):
self.conn = MySqlAConnectionPool()

5.5 封装执行SQL的方法

HandleSqlMysqlA.py

# -*- coding:utf-8 -*-
# @Author: 喵酱
# @time: 2022 - 06 -09
# @File: HandleSqlMysqlA
from deleteSqlApp.conf.dbConnectPool.GetMySqlAConnection import GetMySqlAConnection
from deleteSqlApp.model.DBPoolSet import DBPoolSet
from deleteSqlApp.model.DbInfo import DbInfo
from deleteSqlApp.model.DBPoolSet import DBPoolSet
from deleteSqlApp.utils.YmlUtil import YmlUtil
import os

"""
执行语句查询有结果返回结果没有返回0;
增/删/改返回变更数据条数,没有返回0
"""

class HandleSqlMysqlA:
__instance = None


def __init__(self):
self.db = GetMySqlAConnection().conn

# 封装执行命令
def execute(self, sql, param=None, autoclose=False):
"""
【主要判断是否有参数和是否执行完就释放连接】
:param sql: 字符串类型,sql语句
:param param: sql语句中要替换的参数"select %s from tab where id=%s" 其中的%s就是参数
:param autoclose: 是否关闭连接
:return: 返回连接conn和游标cursor
"""
cursor, conn = self.db.getconn() # 从连接池获取连接
count = 0
try:
# count : 为改变的数据条数
if param:
count = cursor.execute(sql, param)
else:
count = cursor.execute(sql)
conn.commit()
if autoclose:
self.close(cursor, conn)
except Exception as e:
pass
return cursor, conn, count

# 释放连接
def close(self, cursor, conn):
"""释放连接归还给连接池"""
cursor.close()
conn.close()

# 查询所有
def selectall(self, sql, param=None):
cursor = None
conn = None
count = None
try:
cursor, conn, count = self.execute(sql, param)
res = cursor.fetchall()
return res
except Exception as e:
print(e)
self.close(cursor, conn)
return count

# 查询单条
def selectone(self, sql, param=None):
cursor = None
conn = None
count = None
try:
cursor, conn, count = self.execute(sql, param)
res = cursor.fetchone()
self.close(cursor, conn)
return res
except Exception as e:
print("error_msg:", e.args)
self.close(cursor, conn)
return count

# 增加
def insertone(self, sql, param):
cursor = None
conn = None
count = None
try:
cursor, conn, count = self.execute(sql, param)
# _id = cursor.lastrowid() # 获取当前插入数据的主键id,该id应该为自动生成为好
conn.commit()
self.close(cursor, conn)
return count
except Exception as e:
print(e)
conn.rollback()
self.close(cursor, conn)
return count

# 增加多行
def insertmany(self, sql, param):
"""
:param sql:
:param param: 必须是元组或列表[(),()]或((),())
:return:
"""
cursor, conn, count = self.db.getconn()
try:
cursor.executemany(sql, param)
conn.commit()
return count
except Exception as e:
print(e)
conn.rollback()
self.close(cursor, conn)
return count

# 删除
def delete(self, sql, param=None):
cursor = None
conn = None
count = None
try:
cursor, conn, count = self.execute(sql, param)
self.close(cursor, conn)
return count
except Exception as e:
print(e)
conn.rollback()
self.close(cursor, conn)
return count

# 更新
def update(self, sql, param=None):
cursor = None
conn = None
count = None
try:
cursor, conn, count = self.execute(sql, param)
conn.commit()
self.close(cursor, conn)
return count
except Exception as e:
print(e)
conn.rollback()
self.close(cursor, conn)
return count

# TODO 查询单条
# sql1 = 'select * from userinfo where name=%s'
# args = 'python'
# ret = db.selectone(sql=sql1, param=args)
# print(ret) # (None, b'python', b'123456', b'0')

# TODO 增加单条
# sql2 = 'insert into hotel_urls(cname,hname,cid,hid,url) values(%s,%s,%s,%s,%s)'
# ret = db.insertone(sql2, ('1', '2', '1', '2', '2'))
# print(ret)

# TODO 增加多条
# sql3 = 'insert into userinfo (name,password) VALUES (%s,%s)'
# li = li = [
# ('分省', '123'),
# ('到达','456')
# ]
# ret = db.insertmany(sql3,li)
# print(ret)

# TODO 删除
# sql4 = 'delete from userinfo WHERE name=%s'
# args = 'xxxx'
# ret = db.delete(sql4, args)
# print(ret)

# TODO 更新
# sql5 = r'update userinfo set password=%s WHERE name LIKE %s'
# args = ('993333993', '%old%')
# ret = db.update(sql5, args)
# print(ret)

一、搭建django自动化平台(实现一键执行sql)_运维_18

再依次封装剩下的2个数据库就可以了。(感觉现在写的差点意思,以后会重构)

6、测试执行SQL

前后端整体架构完成了,现在试一试数据库的执行sql

一、搭建django自动化平台(实现一键执行sql)_自动化_19

# -*- coding:utf-8 -*-
# @Author: 喵酱
# @time: 2022 - 06 -09
# @File: test_mysql_connect.py
from deleteSqlApp.utils.HandleSqlMysqlC import HandleSqlMysqlC
from deleteSqlApp.utils.HandleSqlMysqlA import HandleSqlMysqlA
from deleteSqlApp.model.DBPoolSet import DBPoolSet
from deleteSqlApp.model.DbInfo import DbInfo
from deleteSqlApp.utils.YmlUtil import YmlUtil

def test_mysql_connet():
# 建立a 的连接池
a_mysql_db = HandleSqlMysqlA()

# 建立c的连接池
c_mysql_db = HandleSqlMysqlC()

# a 执行查询sql
a_sql = "SELECT * FROM a.client WHERE id = %s"
param = "1"
param1 = "2"
result1 = a_mysql_db.selectone(a_sql, param)
result2 = a_mysql_db.selectone(a_sql, param1)

print("a 执行查询sql", result1)
print("a 执行查询sql", result2)

# c 执行查询sql
c_sql = "SELECT * FROM c.act WHERE id = %s"
param3 = "34"
param4 = "35"
result3 = c_mysql_db.selectone(c_sql, param3)
result4 = c_mysql_db.selectone(c_sql, param4)

print("result3", result3)
print("result4", result4)
if __name__ == '__main__':
test_mysql_connet()
print("-----------------")
test_mysql_connet()