前面已经学习了在Django里面如何对单表的操作,同时也学习了1对多(单个外键)的表的操作。接下来,我们看看多对多(多个外键)的关系如何创建和管理。
比如说,我们有一个主机表,也有一个应用程序表,一个主机可以对应多个程序,一个程序也可以对应多个主机,这是一个典型的多对多的结构。一般来说,我们会在数据库里创建一个中间的表,分别和这两个表进行外键关联。
例1. 手动的定义一个HostToApp表,关联到Host和Application表,这样一来,如果我希望创建一个新的关联,我直接对这个中间的表进行操作即可
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')
class Application(models.Model):
name = models.CharField(max_length=32)
class HostToApp(models.Model):
hobj = models.ForeignKey(to='Host',to_field='nid')
aobj = models.ForeignKey(to='Application',to_field='id')
#创建一个新的关联关系
HostToApp.objects.create(hobj_id=1,aobj_id=2)
例2. 除了手动创建一个关系表,我们还可以让系统自动生成一个
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')class Application(models.Model): name = models.CharField(max_length=32) r = models.ManyToManyField("Host")
执行manage.py migrate和 manage.py migration 之后,查看数据库,可以看见自动生成了一个关系表app01_application_r, 里面有3个字段,一个id和两个外键
这个自动生成的表,在Django里面不能直接像单表一样操作,因为系统并不知道他的‘存在’,因此只能通过间接的操作。
间接操作:
查询
//这里首先获取app_id=1的对象,后面的操作都是基于这个前提来的
obj=models.Application.objects.get(id=1)
obj.name
obj.r.all() //类似单表操作
增加
obj.r.add(1) //添加app_id=1, host_id=1的记录
obj.r.add(2,3,4) //直接添加多个值
obj.r.add(*[1,2,3,4]) // 直接通过列表的格式添加多个值
删除,格式和增加一样
obj.r.remove(1)
obj.r.remove(2,3)
obj.r.remove(*[2,3,4])
obj.r.clear() //删除所有app_id=1的关系
修改
obj.r.set([2,3,4]) //直接设置app_id=1, host_id=2,3,4,注意列表前面没有星号
例3. 下面看个实例
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')class Application(models.Model): name = models.CharField(max_length=32) r = models.ManyToManyField("Host")
urls.py ( 里面有 views.app和 views.ajax_add_app,对比ajax和普通使用)
from django.conf.urls import urlfrom django.contrib import adminfrom app01 import viewsurlpatterns = [ url(r'^admin/', admin.site.urls), url(r'^business$', views.business), url(r'^host$', views.host), url(r'^test_ajax$', views.test_ajax), url(r'^app$', views.app), url(r'^ajax_add_app$', views.ajax_add_app),
views.py 注意获取用户输入的新的App名称和这个App所对应的主机列表,分别在不同的表进行创建新数据
def app(request): if request.method == "GET": app_list = models.Application.objects.all() host_list = models.Host.objects.all() return render(request,'app.html',{"app_list": app_list,'host_list': host_list}) elif request.method == "POST": app_name = request.POST.get('app_name') host_list = request.POST.getlist('host_list') print(app_name,host_list) #直接obj就是创建的新数据的对象 obj = models.Application.objects.create(name=app_name) #间接添加列表,前面加* obj.r.add(*host_list) return redirect('/app') def ajax_add_app(request): ret = {'status':True, 'error':None, 'data': None} app_name = request.POST.get('app_name') host_list = request.POST.getlist('host_list') obj = models.Application.objects.create(name=app_name) obj.r.add(*host_list) return HttpResponse(json.dumps(ret))
app.html,主要界面是就是显示当前数据和一个模态对话框。点击添加,弹出对话框,然后输入App和对应的主机名;后台获取之后添加返回页面
<!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{ 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> </tr> </thead> <tbody> {% for app in app_list %} <tr aid="{{ app.id }}"> <td>{{ 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> </td> </tr> {% endfor %} </tbody> </table> <div class="shade hide"></div> <div class="add-modal hide"> <form id="add_form" method="POST" action="/app"> <div class="group"> <input id="app_name" type="text" placeholder="应用名称" name="app_name" /> </div> <div class="group"> //multiple允许多选,发送到后台是一个列表,后台通过get_list接收 <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提交" /> </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'); }); //发送一个Ajax请求,序列化的时候,可以直接通过form.serialize(),dataType指定JSON这样就不需要获取之后再做个JSON.parse(data),如果是单个数据,后台可以直接接收,如果存在列表数据,需要指定tradtional是True $('#add_submit_ajax').click(function(){ $.ajax({ url: '/ajax_add_app', // data: {'user': 123,'host_list': [1,2,3,4]}, data: $('#add_form').serialize(), type: "POST", dataType: 'JSON', // 内部 traditional: true, success: function(obj){ console.log(obj); }, error: function () { } }) }); }) </script></body></html>
结果如下所示:
在我们使用模态对话框的时候,使用AJAX的好处是可以实现一些验证的功能;如果我们不在同一个Url里面使用模态对话框,而是新开一个URL来创建数据,可以直接用普通的form提交就行了。模态对话框适合小数据的提交,而新的Url更适合大量数据的提交(比如新开一个页面写博客)
本文出自 “麻婆豆腐” 博客,请务必保留此出处http://beanxyz.blog.51cto.com/5570417/1952243