公司的主营业务是短信,数据库查询占了很大的工作比重。我们的操作是通过navicat连接十多台机器的库,然后连接数据库-输入sql-修改查询条件(比如一长串日期)进行各种查询。高峰期,客服经常给我们转发问题,比如查看客户为什么收不到短信,查询发送记录,某个短信通道的发送量,签名统计等。让我很郁闷的事情最是每次修改手机号、日期、短信频道等条件,键盘噼里啪啦,鼠标点得痛快。查了资料。都说打工人辛苦,打工人累。让我们不要在这种毫无意义的机械重复中浪费我们的工作,是的,即使它更快。想到把常用的sql总结一下写到配置文件里。这些语句是通过网页点击来执行的。在前端,我通过反复试验终于发现,红框中的查询条件可以通过JavaScript的拼接功能进行连接:使用拼接实现搜索条件的动态添加。以上是我的初步想法。研发虽然给客服MM做了查询后台,但是不适合我们这种“查询工程师”。亲手DIY一个?好主意,如何实施。这不着急,我骑着毛驴看剧本——走着看鱼,日月轮转,工作忙着敲棋子,忙着改文,敲着,没了扶持其他技术人员,前后端居然都打通了。一时之间,大部分的查询工作,我都可以通过笔记本轻轻“点江山”:不管有多少数据库,有多少语句,我都可以写在配置文件里。在前端可以使用element-ui的“Cascader级联选择器”来按类添加要执行的SQL。下图红框是“Cascader级联选择器”的效果。效果如下图:这是一般情况。如何实现,主要介绍2点:如何连接不同的数据库,如何添加sql查询,文末所有源码都上传到gitee,有兴趣的同学可以看看如何连接不同的数据库1.如图所示如何在前端看到数据库,如何在下拉框中显示数据库信息?首先给每个数据库取一个英文名,写在配置文件中,通过configparser模块读取fromrest_framework.viewsimportAPIViewimportconfigparserdefread_cfg(name):cfg=configparser.RawConfigParser()cfg.read(settings.CONFIG_PATH,encoding='utf-8')returncfg.items(name)类get_dbs(APIView):defget(self,request,*args,**kwargs):dbs=read_cfg('db')dbs_list=[]d={}fork,vindbs:d['label']=kd['value']=vdbs_list.append(d)d={}returnJsonResponse({'code':1,'data':dbs_list})然后配置前端,一打开页面就获取数据库信息exportdefault{created(){this.get_dbs()},}asyncget_dbs(){//this.$http.get('dbs'),dbs是获取数据库信息的接口地址const{data:res}=awaitthis.$http.get('dbs')if(res.code!==1){returnthis.$message.error('无法获取dbs')}this.operateFormLabel[0].children=res.data},2。让django处理来自前端的数据库信息。公司数据库是oracle,通过堡垒机,所以使用cx_Oracle和sshtunnel模块kwargs):data=json.loads(request.body.decode('utf-8'))db=data['db']#根据下拉框选择的数据库名,配置要连接的数据库ifnotdb:returnJsonResponse({'code':0,'msg':"Pleaseselectadatabase"})ifdb=='lt1':conn=('192.168.2.1','ms','sgate;Normal;sms')elifdb=='lt2':conn=('192.168.2.6','ms2','sgate;Normal;sms2')#sshtunnel使用SSHTunnelForwarder(('堡垒机地址',端口),ssh_username="ssw",ssh_password="1223456",remote_bind_address=(conn,1521))作为服务器:conn=(conn[1],'123456','127.0.0.1:%d/%s'%(server.local_bind_port,conn[2]))xconn=连接器on(*conn)cursor=xconn.cursor()#创建一个新游标cursor.execute(sql)#执行sql语句ret=cursor.fetchall()cursor.close()xconn.close()这样就可以显示了在页面下拉框中,自由连接数据库。接下来就是如何添加sql了。请看下面的例子。如何添加一条sql查询前端操作1、如何在前端看到如图,如何让这条sql显示在前端?一步到位,在messages.vue中添加级联菜单的一级菜单检查和二级菜单服务器casecadeFormLabel:[{model:'weekly_check',label:'inspection',children:[{label:'Server',value:'inspect'},]}],仅仅显示是不够的,每条语句显示的字段不一样,需要单独定义。2.单独定义要显示的header字段上面说到,在messages.vue中添加inspectheader字段,设置this.tableLabel=this.inspectLabel。为每条sql语句设置不同的头字段赋值给this.tableLabel,这样不同的sql可以显示不同的字段inspectLabel:[{prop:'ip',label:'server',width:120},{prop:'cpu',label:'CPUusage',width:70},{prop:'mem',label:'内存使用',width:70},{prop:'disk',label:'diskUsage',width:230},{prop:'vda1',label:'/dev/vda1usage',width:100},{prop:'vdb1',label:'/dev/vdb1usage',width:100},{prop:'network',label:'NetworkConnection',width:60},{prop:'service',label:'ServiceCheck',width:165},{prop:'url',label:'siteinspection',width:165},{prop:'create_time',label:'date',width:70}],...如果(this.operateForm.sql==='inspect'){this.tableLabel=this.inspectLabel}3.动态添加搜索条件如图所示,方框内的三个搜索条件是由JavaScript的拼接功能生成的。通过它,我们可以为每条语句定义不同的搜索条件。在commonForm.vue中编辑://如果点击的sql是“inspect”,则在页面添加3个搜索条件,分别是网络连接、服务检查、时间范围exportdefault{data(){return{network:{model:'network',label:'网络连接'},service:{model:'service',label:'ServiceCheck'},timerange:{model:'timerange',label:'period',type:'date'},}},方法:{handleChange(){if(this.selectedKeys==='inspect'){this.formLabel.splice(1,1,this.network)this.formLabel.splice(2,1,this.service)this.formLabel.splice(3,1,this.timerange)}}}}接下来是后端操作。后端操作1.读取sql首先将sql命名为“inspect”。config.cfg-[sql]填写要执行的sql语句old_views.py-read_cfg函数,get_sql函数读取config.cfg中的sql语句,例如读取名为“inspect”的语句,返回结果是这样的['selectproject,ip,cpu,mem,disk,vda1,vdb1,network,service,url,create_time\nfromweekly_check\nwherecreate_timeBETWEEN{1}AND{2}']importconfigparserdefread_cfg(name):cfg=configparser.RawConfigParser()cfg.read(settings.CONFIG_PATH,encoding='utf-8')returncfg.items(name)#读取config.cfg中的sql语句,比如读取名为“inspect”的语句,返回结果就像这样#['selectproject,ip,cpu,mem,disk,vda1,vdb1,network,service,url,create_time\nfromweekly_check\nwherecreate_timeBETWEEN{1}AND{2}']defget_sql(sqlname):data=read_cfg('sql')sql=[item[1]foritemindataifsqlname==item[0]]returnsql[0]2.处理页面提交的sql语句,然后提交页面的sql对过来的语句进行处理:比如修改日期,修改where条件,添加搜索条件等]field_dict={'network':network,'service':service}forkey,valueinfield_dict.items():print('len(value)',len(value))field.append(key)condition.append("{0}like'%{1}%'and".format(key,value))ifsqlname=='inspect':print('get_sql(sqlname)',get_sql(sqlname).format(','.join(field),start,end))print('condition',condition)sql=get_sql(sqlname).format(','.join(field),start,end).replace('where','where{0}').format(''.join(condition))3.数据转换字典(('农林牧渔','172.16.1.6','2.05','24.59','/dev/vda1used:7.9Gnouse:30G','19.75','74.11','Exception','Bootlog:OK'),)这是从数据库返回的数据。类型是一个元组。需要转成字典,给value加上key,方便前端识别。如{'项目名称':'农林牧渔','ip':'172.16.1.6'}old_views.py-foo函数,bar函数#将参数转换成字典deffoo(**kwargs):#将字典中的datetime时间格式数据转换为字符串Y-%m-%d%X'))returnkwargs#field是选择哪些字段返回给前端defbar(ret,sqlname,field=None,user_id=None):foriteminret:ifsqlname=='检查':obj=foo(project=item[0],ip=item[1],cpu=item[2],mem=item[3],disk=item[4],vda1=item[5],vdb1=item[6],network=item[7],service=item[8],url=item[9],create_time=item[10])yieldobj中bar()函数的主要功能上一步是将从数据库中找到的数据转换成字典。并在value上加一个key,大概过程:importdatetimeret=(('农林牧渔','172.16.1.6','2.05','24.59','/dev/vda1used:7.9Gnouse:30G','19.75','74.11','Exception','Bootlog:OK','',datetime.datetime(2022,8,19,16,17,12)),)obj={}field=['network','service']foriteminret:fori,vinenumerate(field):obj[v]=item[i]print(obj)输出是:{'network':'Agriculture,林牧Fishing','service':'172.16.1.6'}这里增加了一个sql查询
