前面已经学习了在Django里面如何对单表的操作,同时也学习了1对多(单个外键)的表的操作。接下来,我们看看多对多(多个外键)的关系如何创建和管理。
比如说,我们有一个主机表,也有一个应用程序表,一个主机可以对应多个程序,一个程序也可以对应多个主机,这是一个典型的多对多的结构。一般来说,我们会在数据库里创建一个中间的表,分别和这两个表进行外键关联。
例1. 手动的定义一个HostToApp表,关联到Host和Application表,这样一来,如果我希望创建一个新的关联,我直接对这个中间的表进行操作即可
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
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. 除了手动创建一个关系表,我们还可以让系统自动生成一个
1
2
3
4
5
6
7
8
9
10
|
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
1
2
3
4
5
6
7
8
9
10
|
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和普通使用)
1
2
3
4
5
6
7
8
9
10
|
from
django.conf.urls
import
url
from
django.contrib
import
admin
from
app01
import
views
urlpatterns
=
[
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所对应的主机列表,分别在不同的表进行创建新数据
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
|
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和对应的主机名;后台获取之后添加返回页面
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
|
<!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更适合大量数据的提交(比如新开一个页面写博客)