Django之ORM操作(八)
一、结合ajax实现主机管理
1.1 models.py设置:
class Host(models.Model): nid = models.AutoField(primary_key=True) hostname = models.CharField(max_length=32,db_index=True) ip = models.GenericIPAddressField(protocol='ipv4',db_index=True) port = models.IntegerField() b = models.ForeignKey(to="Business",to_field='id',on_delete=models.CASCADE)
python manage.py makemigrations
python manage.py migrate
1.2 urls.py设置:
url(r'^host$', views.host), url(r'^test_ajax$', views.test_ajax), url(r'^test_update_ajax$', views.test_update_ajax), url(r'^test_delete_ajax$', views.test_delete_ajax),
1.3 views.py设置:
from django.shortcuts import render,HttpResponse,redirect from cmdb import models import json def host(request): if request.method == "GET": v1 = models.Host.objects.filter(nid__gt=0) v2 = models.Host.objects.filter(nid__gt=0).values('nid','hostname','b_id','b__caption') v3 = models.Host.objects.filter(nid__gt=0).values_list('nid','hostname','b_id','b__caption') b_list = models.Business.objects.all() return render(request, 'host.html', {'v1': v1,'v2': v2,'v3': v3,'b_list':b_list}) elif request.method == "POST": h = request.POST.get('hostname') i = request.POST.get('ip') p = request.POST.get('port') b = request.POST.get('b_id') # models.Host.objects.create(hostname=h, # ip=i, # port=p, # b=models.Business.objects.get(id=b) # ) models.Host.objects.create(hostname=h, ip=i, port=p, b_id=b ) return redirect('/cmdb/host') def test_ajax(request): ret = {'status': True, 'error': None, 'data': None} #先定义一个字典 try: h = request.POST.get('hostname') i = request.POST.get('ip') p = request.POST.get('port') b = request.POST.get('b_id') if h and len(h) > 5: #如果主机名长度大于5就执行数据库的插入操作 models.Host.objects.create(hostname=h, ip=i, port=p, b_id=b) else: #否则就把字典里面的两个值附加上 ret['status'] = False ret['error'] = "太短了" except Exception as e: #其他的错误也会将这个ret字典赋值 ret['status'] = False ret['error'] = '请求错误' return HttpResponse(json.dumps(ret)) #json.dumps(ret)将字典转换成字符串传递给客户端 def test_update_ajax(request): ret = {'status': True, 'error': None, 'data': None} try: h = request.POST.get('hostname') i = request.POST.get('ip') p = request.POST.get('port') b = request.POST.get('b_id') n = request.POST.get('nid') #print(h,i,p,b,n) if n and len(h) > 5: models.Host.objects.filter(nid=n).update(hostname=h, ip=i, port=p, b_id=b) else: ret['status'] = False ret['error'] = "太短了" except Exception as e: ret['status'] = False ret['error'] = '请求错误' return HttpResponse(json.dumps(ret)) def test_delete_ajax(request): ret = {'status': True, 'error': None, 'data': None} try: n = request.POST.get('nid') #print(n) if n: models.Host.objects.filter(nid=n).delete() else: ret['status'] = False ret['error'] = "没有获取到nid" except Exception as e: ret['status'] = False ret['error'] = '请求错误' #print(ret) #可以后台打印信息来查看是否存在问题 return HttpResponse(json.dumps(ret))
1.4 host.html的设置:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title></title> <style> .hide{ display: none; } .shade{ position: fixed; top: 0; right: 0; left: 0; bottom: 0; background: black; opacity: 0.6; z-index: 100; } .add-modal,.edit-modal,.delete-modal{ position: fixed; height: 300px; width: 400px; top:100px; left: 50%; z-index: 101; border: 1px solid red; background: white; margin-left: -200px; } .delete:focus{ background: red; } </style> </head> <body> <h1>主机列表(对象)</h1> <div> <input id="add_host" type="button" value="添加" /> </div> <table border="1"> <thead> <tr> <th>序号</th> <th>主机名</th> <th>IP</th> <th>端口</th> <th>业务线名称</th> <th>操作</th> </tr> </thead> <tbody> {% for row in v1 %} <tr hid="{{ row.nid }}" bid="{{ row.b_id }}"> <td >{{ forloop.counter }}</td> <td >{{ row.hostname }}</td> <td>{{ row.ip }}</td> <td >{{ row.port }}</td> <td>{{ row.b.caption }}</td> <td> <a class="edit">编辑</a>|<a class="delete" >删除</a> </td> </tr> {% endfor %} </tbody> </table> <div class="shade hide"></div> <div class="add-modal hide"> <form id="add_form" method="POST" action="/cmdb/host"> <div class="group"> <input id="host" type="text" placeholder="主机名" name="hostname" /> </div> <div class="group"> <input id="ip" type="text" placeholder="IP" name="ip" /> </div> <div class="group"> <input id="port" type="text" placeholder="端口" name="port" /> </div> <div class="group"> <select id="sel" name="b_id"> {% for op in b_list %} <option value="{{ op.id }}">{{ op.caption }}</option> {% endfor %} </select> </div> <input id="post_submit" type="submit" value="提交"/> <a id="ajax_submit" >悄悄提交</a> <input id="cancel" type="button" value="取消" /> <span id="erro_msg" style="color: red"></span> </form> </div> <div class="edit-modal hide"> <form id="edit_form" method="POST" action="/cmdb/host"> <input type="text" name="nid" style="display:none" /> <a>主机名:</a><input type="text" placeholder="主机名" name="hostname" /> <br/> <a>主机IP:</a><input type="text" placeholder="IP" name="ip" /> <br/> <a>主机端口:</a><input type="text" placeholder="端口" name="port" /> <br/> <a>所属部门:</a><select name="b_id"> {% for op in b_list %} <option value="{{ op.id }}">{{ op.caption }}</option> {% endfor %} </select> <br/> <a id="ajax_submit_edit" >确认编辑</a> <a id="err_msg" style="color: red"></a> </form> </div> <div class="delete-modal hide"> <form id="delete_form" method="POST" action="/cmdb/host_delete"> <input type="text" name="nid" style="display:none" /> {# 注意这里name="nid"跟其他的有些不一样,要可以更改,所以没有disabled=disabled; 不可以更改jquery就没法通过val()给其赋值了#} <input type="text" disabled=disabled; placeholder="主机名" name="hostname" /> <input type="text" disabled=disabled; placeholder="IP" name="ip" /> <input type="text" disabled=disabled; placeholder="端口" name="port" /> <select disabled=disabled; name="b_id"> {% for op in b_list %} <option value="{{ op.id }}">{{ op.caption }}</option> {% endfor %} </select> <a id="ajax_submit_delete" >确认删除</a> <a id="err_msg" style="color: red"></a> </form> </div> <script src="/static/jquery-1.12.4.js"></script> <script> $(function() { $('#add_host').click(function () { $('.shade,.add-modal').removeClass('hide'); }); $('#cancel').click(function () { $('.shade,.add-modal').addClass('hide'); }); $('#ajax_submit').click(function () { $.ajax({ url: "/cmdb/test_ajax", type: 'POST', //data: {'hostname': $('#host').val(), 'ip': $('#ip').val(), 'port': $('#port').val(), 'b_id': $('#sel').val()}, data: $('#add_form').serialize(), #把数据发送到后端。serialize()方法通过序列化表单值创建URL编码文本字符串。序列化的值可在生成 AJAX请求时用于URL查询字符串中。 success: function (data) { var obj = JSON.parse(data); #将后台发送过来的JSON类型的字符串转换为JSON对象 if (obj.status) { #如果obj.status是True,就执行下面的当前页面刷新操作。 location.reload(); } else { #否则将#erro_msg的text值赋值为后端发过来的obj.error对应的值 $('#erro_msg').text(obj.error); } } }) }); $('.edit').click(function () { $('.shade,.edit-modal').removeClass('hide'); var bid = $(this).parent().parent().attr('bid'); #$(this)是当前元素被jQuery处理的对象,也就是$(this)表示当前被点击的<a class="edit">编辑</a>,那么它的parent()父元素就是上层的td,然后再parent()父元素就是<tr hid="{{ row.nid }}" bid="{{ row.b_id }}"> #attr()方法设置或返回被选元素的属性值。那么attr('bid')就是取出了当前元素父父元素的b_id的值。同理 attr('hid')就是取出了当前tr元素的nid的值。 var nid = $(this).parent().parent().attr('hid'); var val = $(this).parent().parent().children('td'); #tr元素下有好多td元素,td元素是tr元素的子元素,将tr元素下的所有td的值取出来。children()方法返回返回被选元素的所有直接子元素。 var hostname = val.eq(1).text(); #那么val实际是一个列表索引号从0开始。:eq()选择器选取带有指定index值的元素。eq(1)就是取val得第二个值。text()方法方法设置或返回被选元素的文本内容。 var ip = val.eq(2).text(); var port = val.eq(3).text(); //alert(hostname+" "+ip+" "+port); #可以用弹出框的形式弹出一下取值是否正确 $(this).parent().parent().css("color","red"); ##可以当点击编辑出现弹出框的时候给所选的那行列表再加个css样式,比如说字体颜色变成红色。css() 方法返回或设置匹配的元素的一个或多个样式属性。 $('#edit_form').find('select').val(bid); #find() 方法获得当前元素集合中每个元素的后代,通过选择器、jQuery 对象或元素来筛选。$('#edit_form')这就是获取$('#edit_form')id选择器edit_form #然后查找其后代中的select元素,然后通过val(bid),给其设置值为bid所对应的值。val()方法返回或设置被选元素的值。 $('#edit_form').find('input[name="nid"]').val(nid); $('#edit_form').find('input[name="hostname"]').val(hostname); $('#edit_form').find('input[name="ip"]').val(ip); $('#edit_form').find('input[name="port"]').val(port); $('#ajax_submit_edit').click(function () { $.ajax({ url: "/cmdb/test_update_ajax", type: 'POST', data: $('#edit_form').serialize(), success: function (data) { var obj = JSON.parse(data); if (obj.status) { location.reload(); } else { $('#err_msg').text(obj.error); } } }) }) }); $('.delete').click(function () { $('.shade,.delete-modal').removeClass('hide'); var bid = $(this).parent().parent().attr('bid'); var nid = $(this).parent().parent().attr('hid'); var val = $(this).parent().parent().children('td'); var hostname = val.eq(1).text(); var ip = val.eq(2).text(); var port = val.eq(3).text(); $(this).parent().parent().css("color","blue"); $('#delete_form').find('select').val(bid); $('#delete_form').find('input[name="nid"]').val(nid); $('#delete_form').find('input[name="hostname"]').val(hostname); $('#delete_form').find('input[name="ip"]').val(ip); $('#delete_form').find('input[name="port"]').val(port); $('#ajax_submit_delete').click(function () { $.ajax({ url: "/cmdb/test_delete_ajax", type: 'POST', data: $('#delete_form').serialize(), success: function (data) { var obj = JSON.parse(data); if (obj.status) { location.reload(); } else { $('#err_msg').text(obj.error); } } }) }) }); }); </script> </body> </html>
添加主机测试:
#上面悄悄提交是ajax的提交,这样不会像From表单POST提交那种,提交完页面会刷新一下,这种是页面不动,有错误会提示。
#如果提交没问题,页面会刷新。
编辑主机测试:
#修改的成功了。
删除主机测试:
#从结果可以看到已经没有c3主机名的主机了。
二、ManyToMany多对多关系例子
#就以现在的例子来说,现在所属部门有了Business,主机表有了:Host,前两个基本都是唯一的主机和主机间不一样,部门之间不一样,但是一个主机上面可以有多个应用在跑吧,应用肯定是不同的。那么就是一个主机上面可以跑多个应用,而一个应用比如Web应用可以在两台机器三台甚至更多太Host主机,这就是多对多关系,一个主机可以对应多个应用,一个应用可以对应多台主机。
2.1 models.py的设置:
#自动创建关系表 class Application(models.Model): name = models.CharField(max_length=32) r = models.ManyToManyField("Host") #上面的方式就如下面那种方式,去构建一张新表跟两个表约束关联(自定义关系表): # class HostToApp(models.Model): # hobj = models.ForeignKey(to='Host',to_field='nid') # aobj = models.ForeignKey(to='Application',to_field='id')
python manage.py makemigrations
python manage.py migrate
#从上图可以看到除了cmdb_application表记录应用以外创建了一张新表,cmdb_application_r,它负责通过application_id和cmdb_application表的id字段进行关联,通过host_id与host表的nid字段进行关联。
2.2 cmdb下urls.py的设置:
url(r'^app$', views.app), url(r'^ajax_add_app$', views.ajax_add_app), url(r'^ajax_edit_app$', views.ajax_edit_app), url(r'^ajax_delete_app$', views.ajax_delete_app),
2.3 cmdb下views.py的设置:
def ajax_add_app(request): ret = {'status': True, 'error': None, 'data': None} try: app_name = request.POST.get('app_name') host_list = request.POST.getlist('host_list') #因为主机很多时候可能是一个列表的形式所以用getlist获取多个值 print(app_name, host_list) #可以打印查看一下提交上来的数据 robj = models.Application.objects.filter(name=app_name) #做这个过滤主要是为了防止添加应用名称重复 print (len(robj)) if len(robj) == 0: obj = models.Application.objects.create(name=app_name) #先是在application表里面创建一个新的app_name obj.r.add(*host_list) #然后通过通过obj.r.add,将上面创建的app_name的id跟application_r表里面把主机id列表添加进来进行关联。 return HttpResponse(json.dumps(ret)) elif len(robj) > 0: ret['status'] = False ret['error'] = "应用名重复了" except Exception as e: ret['status'] = False ret['error'] = '请求错误' return HttpResponse(json.dumps(ret)) def ajax_edit_app(request): ret = {'status': True, 'error': None, 'data': None} try: app_name = request.POST.get('app') host_list = request.POST.getlist('host_list') app_id = request.POST.get('nid') # print(request.POST) #如果你不知道提交上面的数据字典的key值可以通过这样打印以下看看要去哪些key对应的值 # print(app_name,app_id,host_list) robj = models.Application.objects.filter(name__icontains="app_name") #name__icontains就比较类似于对name字段进行精确匹配了 # print (len(robj)) if len(robj) == 0: obj = models.Application.objects.get(id=app_id) obj.name = app_name #将application表里的name字段修改成新的应用名称 obj.save() #保存以下 obj.r.set(host_list) #然后将新对应的主机列表去将旧的替换一下,不能直接去修改第三张表,所以要通过这种方式来修改。 #print(ret) return HttpResponse(json.dumps(ret)) elif len(robj) > 0: ret['status'] = False ret['error'] = "应用名重复了" except Exception as e: ret['status'] = False ret['error'] = '请求错误' return HttpResponse(json.dumps(ret)) def ajax_delete_app(request): ret = {'status': True, 'error': None, 'data': None} try: host_list = request.POST.getlist('host_list') app_id = request.POST.get('nid') obj = models.Application.objects.get(id=app_id) #print(host_list,app_id) obj.r.remove(*host_list) #这就是将application表里面的应用所对应的主机列表删除掉 models.Application.objects.filter(id=app_id).delete() #然后就可以将application表里面的应用名称删除掉了。 return HttpResponse(json.dumps(ret)) except Exception as e: ret['status'] = False ret['error'] = '请求错误' return HttpResponse(json.dumps(ret))
django中查询条件对应的sql:
operators = { 'exact': '= %s', 'iexact': 'LIKE %s', 'contains': 'LIKE BINARY %s', 'icontains': 'LIKE %s', 'regex': 'REGEXP BINARY %s', 'iregex': 'REGEXP %s', 'gt': '> %s', 'gte': '>= %s', 'lt': '< %s', 'lte': '<= %s', 'startswith': 'LIKE BINARY %s', 'endswith': 'LIKE BINARY %s', 'istartswith': 'LIKE %s', 'iendswith': 'LIKE %s', }
2.4 app.html的设置:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title></title> <style> .host-tag{ display: inline-block; padding: 3px; border: 1px solid red; background-color: palevioletred; } .hide{ display: none; } .shade{ position: fixed; top: 0; right: 0; left: 0; bottom: 0; background: black; opacity: 0.6; z-index: 100; } .add-modal,.edit-modal,.delete-modal{ position: fixed; height: 300px; width: 400px; top:100px; left: 50%; z-index: 101; border: 1px solid red; background: white; margin-left: -200px; } </style> </head> <body> <h1>应用列表</h1> <div> <input id="add_app" type="button" value="添加" /> </div> <table border="1"> <thead> <tr> <td>应用名称</td> <td>应用主机列表</td> <td>编辑管理</td> </tr> </thead> <tbody> {% for app in app_list %} <tr aid="{{ app.id }}"> <td name="app">{{ app.name }}</td> <td> {% for host in app.r.all %} <span class="host-tag" hid="{{ host.nid }}"> {{ host.hostname }} </span> {% endfor %} </td> <td> <a class="edit">编辑</a>|<a class="delete" >删除</a> </td> </tr> {% endfor %} </tbody> </table> <div class="shade hide"></div> <div class="add-modal hide"> <form id="add_form" method="POST" action="/cmdb/app"> <div class="group"> <input id="app_name" type="text" placeholder="应用名称" name="app_name" /> </div> <div class="group"> <p>主机列表:</p> <div> <input type="button" value="全选" id="btall" /> <input type="button" value="取消" id="btnall"/> </div> <select id="host_list" name="host_list" multiple> {% for op in host_list %} <option value="{{ op.nid }}">{{ op.hostname }}</option> {% endfor %} </select> </div> <input type="submit" value="提交" /> <input id="add_submit_ajax" type="button" value="Ajax提交" /> <span id="erro_msg" style="color: red"></span> </form> </div> <div class="edit-modal hide"> <form id="edit_form" method="POST" action="/cmdb/host"> <input type="text" name="nid" style="display:none" /> <input type="text" placeholder="应用名称" name="app" /> <select name="host_list" multiple> {% for op in host_list %} <option value="{{ op.nid }}">{{ op.hostname }}</option> {% endfor %} </select> <a id="ajax_submit_edit" >确认编辑</a> <span id="erro_msg" style="color: red"></span> </form> </div> <script src="/static/jquery-1.12.4.js"></script> <script> $(function(){ $('#add_app').click(function(){ $('.shade,.add-modal').removeClass('hide'); }); $('#cancel').click(function(){ $('.shade,.add-modal').addClass('hide'); }); $('#btall').click(function () { var valt = [] $('#host_list option').each(function () { {#each也是一种循环,是对前面的循环,因为前面有多行option项所以就可以循环起来#} var val = $(this).val(); {#因为循环起来了所以$(this)就代表select列表里面的每一行option,然后val()就是取出其option对应的值#} valt.push(val); {#然后通过.push将每一次取到的val变量所对应的值添加到列表valt中#} {#$(this).attr("selected",true);#} {#上面这种方式默认就是被选中有弊端,因为你点击select可选框中的内容就会退出默认效果那么你再点击全选就不会生效了至少在显示上#} }); {#hostname = $('#host_list option').text();#} {#上面是取出option的文本内容也就是应用名称不过这里用不到#} {#console.log(valt + typeof(valt) + hostname + typeof (hostname));#} {# 这是另外一种打印jquery查询结果,console.log浏览器打印如果内容比较多打印效果比较好#} $('#host_list').val(valt); {#然后给select标签赋值也就是把上面循环得出的列表添加进去,这样select标签就出现了选中效果了#} }); $('#btnall').click(function () { $('#host_list').val([]); {#这句话就好理解了,传一个空列表进去这样就是清空select选中效果#} }); $('#add_submit_ajax').click(function() { $.ajax({ url: '/cmdb/ajax_add_app', // data: {'user': 123,'host_list': [1,2,3,4]}, data: $('#add_form').serialize(), type: "POST", dataType: 'JSON', // 内部,这是另外一种方式直接在内部就将传过来的数据转换成JSON效果 traditional: true, //设置jquery 的 traditional参数 实际上是设置 jQuery.param 的traditional 参数,默认为false,当设置为true后,会导致多层次的对象序列化为[object object](浅序列化) success: function (obj) { if (obj.status) { location.reload(); //location.href = "某个地址" # 跳转 } else { $('#erro_msg').text(obj.error); } } }); }); $('.edit').click(function(){ $('.edit-modal,.shade').removeClass('hide'); var nid = $(this).parent().parent().attr('aid'); //要取出tr的aid属性值也就是aid的值要通过attr()来取 {#alert(aid);#} var hid_list = []; $(this).parent().prev().children().each(function(){ //prev()是统计元素前面的元素 var hid = $(this).attr('hid'); hid_list.push(hid) }); {#var app_name = $(this).parent().parent().children().filter("app").text();#} var app_name = $(this).parent().parent().find('td[name="app"]').text(); $('#edit_form').find('input[name="app"]').val(app_name); //这里都是让模态对话框默认显示一些内容并非最后提交的结果内容 $('#edit_form').find('select').val(hid_list); $('#edit_form').find('input[name="nid"]').val(nid); $('#ajax_submit_edit').click(function() { $.ajax({ url: '/cmdb/ajax_edit_app', // data: {'user': 123,'host_list': [1,2,3,4]}, data: $('#edit_form').serialize(), //获取edit_form表单里面的内容交给data type: "POST", dataType: 'JSON', // 内部 traditional: true, success: function (obj) { console.log(obj); if (obj.status) { location.reload(); } else { $('#erro_msg').text(obj.error); } } }); }); }); $('.delete').click(function(){ var nid = $(this).parent().parent().attr('aid'); var hid_list = []; $(this).parent().prev().children().each(function(){ var hid = $(this).attr('hid'); hid_list.push(hid) }); app_name = $(this).parent().parent().find('td').eq(0).text(); var confirmjg = confirm("真的要删除应用:"+app_name+"业务"); //确认弹窗提示一下是否真的要删除 if (confirmjg) //如果confirmjg返回true就会执行下面的ajax提交操作,如果是false就会回到web页面 $.ajax({ url: '/cmdb/ajax_delete_app', // data: {'user': 123,'host_list': [1,2,3,4]}, data: {'nid':nid,'host_list':hid_list}, //这是手工给data传值得形式 type: "POST", dataType: 'JSON', traditional: true, success: function (obj) { console.log(obj); if (obj.status) { location.reload(); } else { $('#erro_msg').text(obj.error); } } }); }); }); </script> </body> </html>
添加测试:
#从上图可以看到应用名和主机已经关联上了,如果应用名称重复的话会有提示不能提交成功。
编辑测试:
删除测试:
#可以看到peoxy应用删除掉了。