Django基础--3
ORM
django一对多的操作
获取表单的三种方式:views
获取对象 .all() html中获取方式为 row.id,row,host
获取字典 .all().values("字段","字段","字段",……) row.id,row,host
获取元祖 .all().values_list("字段","字段","字段",……) row.0,row.1
#views from django.shortcuts import render from app import models # Create your views here. def home(request): v1 = models.Host.objects.all() print(v1) #<QuerySet [<Host: Host object (1)>, <Host: Host object (2)>, <Host: Host object (3)>]> 对象 v2 = models.Host.objects.all().values("host","port") print(v2) #< QuerySet[{'host': 'a', 'port': 80}, {'host': 'b', 'port': 81}, {'host': 'c', 'port': 82}] > 字典类型 v3 = models.Host.objects.all().values_list("host","port") print(v3) #<QuerySet [('a', 80), ('b', 81), ('c', 82)]> 元祖类型 retur #html中 <body> <h1>主机信息</h1> <ul> {% for row in v1 %} <li>{{ row.id }}--- {{ row.host }}--- {{ row.ip }}--- {{ row.port }}--- {{ row.dept_id }}</li> {% endfor %} </ul> <h1>主机信息</h1> <ul> {% for row in v2 %} <li>{{ row.host }}--- {{ row.port }}</li> {% endfor %} </ul> <h1>主机信息</h1> <ul> {% for row in v3 %} <li>{{ row.0 }}--- {{ row.1 }}</li> {% endfor %} </ul> </body>
一对多的跨表操作
获取对象 filter(id__gte=1)
#views def host(request): v1 = models.Host.objects.filter(id__gte=1) return render(request,"host.html",{"v1":v1}) #urls.py from app import views path('host/', views.host), #host.html <body> <table border="1"> <thead> <tr> <th>id</th> <th>主机名</th> <th>主机ip</th> <th>主机端口</th> <th>业务线</th> <th>部门</th> <th>部门代码</th> </tr> </thead> <tbody> {% for row in v1 %} <tr> <td>{{ row.id }}</td> <td>{{ row.host }}</td> <td>{{ row.ip }}</td> <td>{{ row.port }}</td> <td>{{ row.dept_id }}</td> <td>{{ row.dept.dept }}</td> <td>{{ row.dept.dept_en }}</td> </tr> {% endfor %} </tbody> </table> </body>
以上将所有的数据都显示出来了,然而实际中并用不到这么多,这里可以隐藏两个表的id,因每次都会循环tr,直接在<tr>中定义两个属性放id
这里修改下tbody里面内容即可
<tbody> {% for row in v1 %} <tr hid = "{{ row.id }}" bid = "{{ row.dept.id }}"> <td>{{ row.host }}</td> <td>{{ row.ip }}</td> <td>{{ row.port }}</td> <td>{{ row.dept.dept }}</td> </tr> {% endfor %} </tbody>
获取字典 filter(id__gte=1).values("id","ip", "port", "dept_id","dept__dept")
#views.py #获取类型为字典 v2 = models.Host.objects.filter(id__gte=1).values("id","ip", "port", "dept_id","dept__dept") #print(v2) # <QuerySet [{'ip': '1.1.1.1', 'host': 'a', 'dept__dept': '运维'}, {'ip': '1.1.1.2', 'host': 'b', 'dept__dept': '销售'}, {'ip': '1.1.1.3', 'host': 'c', 'dept__dept': '信息部'}]> for i in v2: print(i["id"],i["ip"],i["port"],i["dept__dept"])
return render(request,"host.html",{"v2":v2})
前端操作: <h2>业务线-字典</h2> <table border="1"> <thead> <tr> <th>主机ip</th> <th>主机端口</th> <th>部门</th> </tr> </thead> <tbody> {% for row in v2 %} <tr hid = "{{ row.id }}" bid = "{{ row.dept_id }}"> <td>{{ row.ip }}</td> <td>{{ row.port }}</td> <td>{{ row.dept__dept }}</td> </tr> {% endfor %} </tbody> </table>
获取元祖 filter(id__gte=1).values_list("id","ip", "port", "dept_id","dept__dept")
v3 = models.Host.objects.filter(id__gte=1).values_list("id","ip", "port", "dept_id","dept__dept") for j in v3: print(j[0],j[1],j[2],j[3],j[4],) return render(request,"host.html",{"v3":v3})
<h2>业务线-元祖</h2> <table border="1"> <thead> <tr> <th>主机ip</th> <th>主机端口</th> <th>部门</th> </tr> </thead> <tbody> {% for row in v3 %} <tr hid = "{{ row.0 }}" bid = "{{ row.3 }}"> <td>{{ row.1 }}</td> <td>{{ row.2 }}</td> <td>{{ row.4}}</td> </tr> {% endfor %} </tbody> </table>
总结
获取数据的3种方法中的第一种,页面中获取到的元素直接是对象,对对象用点就可以进行跨表
另外的两种方法,获取到的不再是对象了,而是字典和元组。这时候取值要传字符串,要跨表就得在字符串中使用双下划线
显示序号-for循环中的forloop
在模板语言的for循环里还有一个forloop,通过这个可以取到到序号:
forloop.counter
:序号,从1开始forloop.counter0
:序号,从0开始forloop.revcounter
:序号,倒序,从1开始forloop.revcounter0
:序号,倒序,从0开始forloop.first
:是否是第一个forloop.last
:是否是最后一个forloop.parentloop
:有嵌套循环的情况下,获取父类的以上6个值。字典的形式,可以继续通过点来取到具体的值
<h2>forloop</h2> <table border="1"> <thead> <tr> <th>forloop.counter </th> <th>forloop.counter0</th> <th>forloop.revcounter</th> <th>forloop.revcounter0</th> <th>forloop.first</th> <th>forloop.last</th> <th>forloop.parentloop</th> <th>主机ip</th> <th>主机端口</th> <th>部门</th> </tr> </thead> <tbody> {% for i in v3 %} {% for row in v3 %} <tr hid = "{{ row.0 }}" bid = "{{ row.3 }}"> <td>{{ forloop.counter }}</td> <td>{{ forloop.counter0 }}</td> <td>{{ forloop.revcounter }}</td> <td>{{ forloop.revcounter0 }}</td> <td>{{ forloop.first}}</td> <td>{{ forloop.last}}</td> <td>{{ forloop.parentloop}}</td> <td>{{ row.1 }}</td> <td>{{ row.2 }}</td> <td>{{ row.4}}</td> </tr> {% endfor %} {% endfor %} </tbody> </table>
上面使用了两层循环,是为了显示forloop.parentloop
的效果,其他效果只用看上面三行就行,后面六行都是重复的内容
下拉框-添加数据的示例
添加静态文件到settings
STATICFILES_DIRS = ( os.path.join(BASE_DIR,"static"), )
host.html中
<head> <meta charset="UTF-8"> <title>Title</title> <link rel="stylesheet" href="/static/commons.css" /> </head> <body> <h2>业务线-对象</h2> <p> <input class="add" type="button" value="添加" /> </p> <table border="1"> <thead> <tr> <th>序号</th> <th>主机名</th> <th>主机ip</th> <th>主机端口</th> <th>部门</th> </tr> </thead> <tbody> {% for row in v1 %} <tr hid = "{{ row.id }}" bid = "{{ row.dept.id }}"> <td>{{ forloop.counter }}</td> <td>{{ row.host }}</td> <td>{{ row.ip }}</td> <td>{{ row.port }}</td> <td>{{ row.dept.dept }}</td> </tr> {% endfor %} </tbody> </table> <div class="shade hide"></div> <div class="add-host hide"> <form action="/host/" method="post"> <p> <input class="group" type="text" name="host" placeholder="主机名"/> </p> <p> <input class="group" type="text" name="ip" placeholder="ip"/> </p> <p> <input class="group" type="text" name="port" placeholder="端口号"/> </p> <p> <select name="dept_id"> {% for op in dept %} <option value="{{ op.id }}">{{ op.dept }}</option> {% endfor %} </select> </p> <p> <input type="submit" value="提交" /> <input class="cancel" type="button" value="取消" /> </p> </form> </div> <script src="/static/jquery-3.2.1.min.js"></script> <script src="/static/js.js"></script> </body>
static中的css和js
#commons.css .hide{ display: none; } .shade { position: fixed; left: 0; top: 0; right: 0; bottom: 0; background: black; opacity: 0.6; z-index: 100; } .add-host { width: 500px; height: 400px; position: fixed; left: 50%; top: 200px; background: white; z-index: 101; margin-left: -300px; text-align: center; } #js.js $(function () { $(".add").click(function () { $(".shade,.add-host").removeClass("hide") }) $(".cancel").click(function () { $(".shade,.add-host").addClass("hide") }) })
views.py
def host(request): if request.method == "GET": v1 = models.Host.objects.filter(id__gte=1) v2 = models.Host.objects.filter(id__gte=1).values("id","ip", "port", "dept_id","dept__dept") v3 = models.Host.objects.filter(id__gte=1).values_list("id","ip", "port", "dept_id","dept__dept") dept = models.Bussines.objects.all() return render(request,"host.html",{"v1":v1,"v2":v2,"v3":v3,"dept":dept}) elif request.method == "POST": h = request.POST.get("host") i = request.POST.get("ip") p = request.POST.get("port") d = request.POST.get("dept_id") models.Host.objects.create(host = h,ip = i,port = p,dept_id = d) return redirect("/host/") //这里重新获取get请求,从而刷新页面
AJAX
数据验证
这里是在后台验证
模态对话框里提交表单的页面增加一个按钮,然后在jQuery里绑定事件
下面为绑定的事件
$(".ajax").click(function () { $.ajax({ url:"/test/", type:"POST", data:{ "host":$("#host").val(), "ip":$("#ip").val(), "port":$("#port").val(), "dept_id":$("#sel").val()}, success:function (data) { if (data == "OK"){ location.reload() } else { alert(data) location.reload() } } }) })
htm中添加一个按钮
在上个实例中只添加一个按钮 <p> <input type="submit" value="提交" /> <input class="ajax" type="button" value="Ajax提交方式" /> <input class="cancel" type="button" value="取消" /> </p>
urls.py中修改
path('host/', views.host), path('test/', views.test),
views.py在上面的实例中只添加
def test(request): h = request.POST.get("host") i = request.POST.get("ip") p = request.POST.get("port") d = request.POST.get("dept_id") if h and len(h) > 5: models.Host.objects.create(host=h, ip=i, port=p, dept_id=d) return HttpResponse("OK") else: return HttpResponse("太短了")
优化验证
上面的验证只是个很简单的验证,还没考虑类型是否满足数据库的要求。如果数据不符合数据库的数据格式,例如将端口号填写为字符串,此时程序就会报错,报错系统并不会崩溃,我们调试的时候可以看到错误信息,但是客户端是不知道发生了什么的。
下面就通过try来捕获异常,之后可以返回自定义的消息内容,或者也可以把异常信息返回
def test(request): import json dic = {"static":True,"error":None,"data":None} try: h = request.POST.get("host") i = request.POST.get("ip") p = request.POST.get("port") d = request.POST.get("dept_id") if h and len(h) > 5: models.Host.objects.create(host=h, ip=i, port=p, dept_id=d) else: dic["static"] = False dic["error"] = "太短了……" except Exception as e: dic["static"] = False dic["error"] = "请求错误……" return HttpResponse(json.dumps(dic))
上面返回都用HttpResponse()方式返回,并且返回的最好都是一个字典形式的
在提交按钮后面添加个span标签,用来提示用户,提示信息由后台提供
<p> <input type="submit" value="提交" /> <input class="ajax" type="button" value="Ajax提交方式" /> <input class="cancel" type="button" value="取消" /> <span class=error style="color: red"></span> </p>
在AJAX验证中,修改下上个例子的判断
$(".ajax").click(function () { $.ajax({ url:"/test/", type:"POST", data:{ "host":$("#host").val(), "ip":$("#ip").val(), "port":$("#port").val(), "dept_id":$("#sel").val()}, success:function (data) {
//data是服务器发送过来的数据,就是HttpResponse中的内容 //将字符串转为对象:字典 var data = JSON.parse(data) if (data.static){ location.reload() } else { $(".error").text(data.error) } } }) })
序列化返回的消息(JSON)
HttpResponse返回的内容是字符串,使用JSON序列化字符串,就可以返回更多的信息了,并且客户端处理起来也很方便。上面的例子已经这么做了。
1.先导入json模块
2.然后序列化字典 : return HttpResponse(json.dumps(dic))
3.在jQ中处理字符串:使用 JSON.parse() 将字符串转为对象 var data = JSON.parse(data)
4.在jQ中可以直接使用data.key名 获取到值
示例-编辑功能
编辑页面和添加页面内容基本相同,只要把上面的添加复制过来稍加修改即可,样式用一样就行
<div class="edit-form hide"> <form action="/host/" method="post" hid = "" > <p> <input type="text" name="id" style="display: none" /> </p> <p> <input type="text" name="host" placeholder="主机名"/> </p> <p> <input type="text" name="ip" placeholder="ip"/> </p> <p> <input type="text" name="port" placeholder="端口号"/> </p> <p> <select name="dept_id"> {% for op in dept %} <option value="{{ op.id }}">{{ op.dept }}</option> {% endfor %} </select> </p> <p> <input class="ajax" type="button" value="编辑" /> <input class="cancel" type="button" value="取消" /> </p> </form> </div>
然后就是处理函数了
$(".edit").click(function () {
$(".shade,.edit-form").removeClass("hide");
//获取部门id
var bid = $(this).parent().parent().attr("bid");
//获取主机id
var hid = $(this).parent().parent().attr("hid");
$(".edit-form").find("select").val(bid);
$(".edit-form").find("input[name = 'id']").val(hid);
//获取编辑框中的值
var obj = $(this).parent().siblings("td").first();
//将表格中的值循环填入编辑框
$(".edit-form :text").each(function () {
$(this).val(obj.text());
obj = obj.next();
});
});
$(".ajax1").click(function () {
$.ajax({
url:"/edit/",
type:"POST",
//将edit-form表单的值打包发送到后台
data:$(".edit_form").serialize(),
success:function (data) {
//将字符串转为对象:字典
var data = JSON.parse(data)
console.log(data.static)
if (data.static){
location.reload()
}
else {
$(".error").text(data.error)
}
}
});
});
$(".cancel").click(function () {
$(".shade,.edit-form").addClass("hide")
});
点击编辑按钮,之后去掉隐藏的样式
因为是编辑,因此要获取到当前点击的hid和nid,这里可以通过点击编辑按钮是获取,因为在tr标签我们设置了hid和nid
然后添加一个带id的输入框,默认肯定是不能让用户修改的,隐藏把该标签隐藏掉
然后是下拉框,进入编辑页面,默认是要显示当前的部门的,这里可以通过bid来修改值,使其显示相应的部门
在使用Ajax时,我们有更简单的方式获取form表单的内容,就是使用serialize(),他可以将form表单的内容都打包一起
然后我们使用Ajax发送到后台
data:$(".edit-form").serialize(),
后台获取数据后处理如下
def edit(request): dic = {"static": True, "error": None, "data": None} try: id = request.POST.get("id") edit_data = { 'host':request.POST.get("host"), 'ip':request.POST.get("ip"), 'port':request.POST.get("port"), 'dept_id':request.POST.get("dept_id"), } for i ,j in edit_data.items(): if j: models.Host.objects.filter(id = id).update(**edit_data) else: dic["static"] = False dic["error"] = "内容错误……" except Exception as e: dic["static"] = False dic["error"] = "内容错误……" return HttpResponse(json.dumps(dic))
url中添加
path('edit/', views.edit),
示例-删除功能
上面的例子中已经添加了删除按钮,这里就不用改html了
只需要在js中处理点击删除按钮的操作
$('.delete').click(function () { var hid = $(this).parent().parent().attr('hid') $.ajax({ url:"/delete/", type:"POST", //将edit-form表单的值打包发送到后台 data:{"hid":hid}, success:function (data) { //将字符串转为对象:字典 var data = JSON.parse(data) console.log(data.static) if (data.static){ location.reload() } else { $(".error").text(data.error) } } }) });
先获取hid,然后将hid传给后台
在urls中添加
path('delete/', views.delete),
后台处理如下
def delete(request): dic = {"static": True, "error": None, "data": None} try: id = request.POST.get("hid") if id: models.Host.objects.filter(id=id).delete() else: dic["static"] = False dic["error"] = "内容错误……" except Exception as e: dic["static"] = False dic["error"] = "内容错误……" return HttpResponse(json.dumps(dic))
获取到nid,根据nid查找数据库相应的对象,删除掉
外键操作-多对多
多对多关系表的创建--两种方式
一般两种方式都用,自定义方式可以关联多个信息,自动创建的关联表只有三个属性:第三张表的id,host表的id,应用表的id
创建应用对应主机的关系表,一个应用可以属于多台主机,一个主机可以多个应用
先创建一个应用表
class Apply(models.Model): name = models.CharField(max_length=32)
然后创建主机和应用的关联表
1.自定义关系表
class Host_to_apply(models.Model): hobj = models.ForeignKey(Host,to_field='id',on_delete=models.CASCADE) aobj = models.ForeignKey(Apply,to_field='id',on_delete=models.CASCADE)
2.自动创建关系表
通过创建应用表时自动创建
class Apply(models.Model): name = models.CharField(max_length=32) # 第二种方式 r = models.ManyToManyField('Host')
自动创建只能帮我们创建一张3个字段的表:自增id,关联表的主键,被关联表的主键。如果想加额外的数据就只能用自定义关系表来创建额外的字段了。
设置关联关系
ORM都是通过类来进行数据库操作的。自定义关系表,直接可以获得结合表的类,直接操作结合表就可以进行数据库操作了
Host_to_apply.objects.create(aobj_id=1, hobj_id=1)
对于自动创建关联关系表,由于并没有结合表的类,无法直接对结合表进行操作。这里可以获取到对象,比如客户表id=1的那条数据对象,使用提供的方法对这个对象的关联系进行操作,添加、删除、清除、设置。
obj = Apply.objects.get(id=1) # 先获取到一个对象,下面都是对id=1的关联关系进行操作 obj.r.add(1) # 添加一个关系 obj.r.add(2, 3, 4) # 多个参数添加多个关系 obj.r.add(*[2, 3, 4]) # 通过列表添加多个关系 obj.r.remove(1) # 删除一个关系,同样支持多个参数或列表 obj.r.clear() # 清除这个id的所有的关系 obj.r.set([3, 5, 7]) # 设置关系。这个id的其他关系都会清除,最后只有这个列表中的关系。相当于先清除在添加。这里没星号
上面没有获取的方法,获取的方法和之前获取数据的方法一样。models.Apply.objects 后面能使用什么方法,这里的obj就可以使用什么方法。比如:.all() 所有被关联的表的对象。all() 方法获取到的一定是一个QuerySet对象,在这里里面的每个元素是一个被关联的表 Apply 的对象。
多对多实例
应用到:查询,添加
apply.html中
<head> <meta charset="UTF-8"> <title>Title</title> <link rel="stylesheet" href="/static/commons.css" /> </head> <body> <h2>应用列表</h2> <p> <input class="add" type="button" value="添加" /> </p> <table border="1"> <thead> <tr> <th>应用名称</th> <th>应用主机名称</th> </tr> </thead> <tbody> {% for row in apply_list %} <tr aid = "{{ row.id }}" > <td>{{ row.name }}</td> <td> {% for i in row.r.all %} <span class="td">{{i.host}}</span> {% endfor %} </td> </tr> {% endfor %} </tbody> </table> <div class="shade hide"></div> <div class="add-host hide"> <form action="/apply/" method="post"> <p> <input id="name" class="group" type="text" name="name" placeholder="应用名称"/> </p> <p> <select id="apply_name" name="apply_name" multiple> {% for op in host_list %} <option value="{{ op.id }}">{{ op.host }}</option> {% endfor %} </select> </p> <p> <input class="ajax2" type="button" value="Ajax提交" /> <input class="cancel" type="button" value="取消" /> <span class=error style="color: red"></span> </p> </form> </div> <script src="/static/jquery-3.2.1.min.js"></script> <script src="/static/js.js"></script> </body>
URLS.py中添加
path('apply/', views.apply),
js中添加
$(".add").click(function () {
$(".shade,.add-host").removeClass("hide")
});
$(".ajax2").click(function () {
$.ajax({
url:"/apply/",
type:"POST",
data:$(this).parent().parent().serialize(),
//data是服务器发送过来的数据,就是HttpResponse中的内容
datatype:JSON,//这里用jq获取就转为对象,下面就不需要在使用JSON.parse(data)
success:function (data) {
//将字符串转为对象:字典
if (data.static){
location.reload()
}
else {
$(".error").text(data.error)
}
},
error:function () {
//用来处理
}
});
});
$(".cancel").click(function () {
$(".shade,.add-host").addClass("hide")
});
datatype:JSON,//这里用jq获取就转为对象,下面就不需要在使用JSON.parse(data)
views.py
def apply(request): if request.method == 'GET': apply_list = models.Apply.objects.all() host_list = models.Host.objects.all() # for i in apply_list: # print(i.name,i.r.all()) return render(request, "apply.html", {'apply_list': apply_list, 'host_list': host_list}) elif request.method == 'POST': dic = {"static": True, "error": None, "data": None} try: name = request.POST.get("name") host_list = request.POST.getlist('apply_name') if name: obj = models.Apply.objects.create(name = name) obj.r.add(*host_list) else: dic["static"] = False dic["error"] = "内容错误……" except Exception as e: dic["static"] = False dic["error"] = "内容错误……" print(e) return HttpResponse(json.dumps(dic))