Django 数据库表多对多的创建和增删改查

简介:

前面已经学习了在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()
     =  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()
     =  models.ForeignKey(to = "Business" , to_field = 'id' )
 
class  Application(models.Model):
     name  =  models.CharField(max_length = 32 )
     =  models.ManyToManyField( "Host" )


执行manage.py migrate和 manage.py migration 之后,查看数据库,可以看见自动生成了一个关系表app01_application_r, 里面有3个字段,一个id和两个外键

wKioL1l-gUfDqDrfAAD1MClx1WM422.jpg

这个自动生成的表,在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()
     =  models.ForeignKey(to = "Business" , to_field = 'id' )
 
class  Application(models.Model):
     name  =  models.CharField(max_length = 32 )
     =  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 >


结果如下所示:

wKiom1l-jHSRyfXLAAD35VHqDVU630.jpg

wKioL1l-jHWSWweBAADwlLLO5-s108.jpg

wKioL1l-jHaBfgaXAAEKbhBF4iM450.jpg


在我们使用模态对话框的时候,使用AJAX的好处是可以实现一些验证的功能;如果我们不在同一个Url里面使用模态对话框,而是新开一个URL来创建数据,可以直接用普通的form提交就行了。模态对话框适合小数据的提交,而新的Url更适合大量数据的提交(比如新开一个页面写博客)






本文转自 beanxyz 51CTO博客,原文链接:http://blog.51cto.com/beanxyz/1952243,如需转载请自行联系原作者

目录
相关文章
|
30天前
|
前端开发 Java 数据库连接
javamvc配置,增删改查,文件上传下载。
【10月更文挑战第4天】javamvc配置,增删改查,文件上传下载。
37 1
|
1月前
|
存储 NoSQL API
使用Py2neo进行Neo4j图数据库的增删改查操作
使用Py2neo进行Neo4j图数据库的增删改查操作
56 5
|
1月前
|
数据可视化 API PHP
低代码开发工具-学生管理系统-老师管理增删改查实现
低代码开发工具-学生管理系统-老师管理增删改查实现
30 5
|
1月前
|
关系型数据库 MySQL Java
Django学习二:配置mysql,创建model实例,自动创建数据库表,对mysql数据库表已经创建好的进行直接操作和实验。
这篇文章是关于如何使用Django框架配置MySQL数据库,创建模型实例,并自动或手动创建数据库表,以及对这些表进行操作的详细教程。
61 0
Django学习二:配置mysql,创建model实例,自动创建数据库表,对mysql数据库表已经创建好的进行直接操作和实验。
|
2月前
|
SQL 关系型数据库 MySQL
学成在线笔记+踩坑(3)——【内容模块】课程分类查询、课程增改删、课程计划增删改查,统一异常处理+JSR303校验
课程分类查询、课程新增、统一异常处理、统一封装结果类、JSR303校验、修改课程、查询课程计划、新增/修改课程计划
学成在线笔记+踩坑(3)——【内容模块】课程分类查询、课程增改删、课程计划增删改查,统一异常处理+JSR303校验
|
22天前
|
JavaScript 前端开发 测试技术
[新手入门]todolist增删改查:vue3+ts版本!
【10月更文挑战第15天】[新手入门]todolist增删改查:vue3+ts版本!
|
2月前
|
SQL 关系型数据库 MySQL
ThinkPHP6 连接使用数据库,增删改查,find,select,save,insert,insertAll,insertGetId,delete,update方法的用法
本文介绍了在ThinkPHP6框架中如何连接和使用数据库进行增删改查操作。内容包括配置数据库连接信息、使用Db类进行原生MySQL查询、find方法查询单个数据、select方法查询数据集、save方法添加数据、insertAll方法批量添加数据、insertGetId方法添加数据并返回自增主键、delete方法删除数据和update方法更新数据。此外,还说明了如何通过数据库配置文件进行数据库连接信息的配置,并强调了在使用Db类时需要先将其引入。
ThinkPHP6 连接使用数据库,增删改查,find,select,save,insert,insertAll,insertGetId,delete,update方法的用法
|
1月前
|
SQL Go 数据库
【速存】深入理解Django ORM:编写高效的数据库查询
【速存】深入理解Django ORM:编写高效的数据库查询
57 0
|
1月前
|
Java API 数据库
Data jpa 增删改查的方法分别有哪些
Data jpa 增删改查的方法分别有哪些
|
2月前
|
数据库 Python
django中数据库外键可以自定义名称吗
django中数据库外键可以自定义名称吗