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,如需转载请自行联系原作者

目录
相关文章
|
22天前
|
SQL 关系型数据库 MySQL
学成在线笔记+踩坑(3)——【内容模块】课程分类查询、课程增改删、课程计划增删改查,统一异常处理+JSR303校验
课程分类查询、课程新增、统一异常处理、统一封装结果类、JSR303校验、修改课程、查询课程计划、新增/修改课程计划
学成在线笔记+踩坑(3)——【内容模块】课程分类查询、课程增改删、课程计划增删改查,统一异常处理+JSR303校验
|
9天前
|
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方法的用法
|
8天前
|
数据库 Python
django中数据库外键可以自定义名称吗
django中数据库外键可以自定义名称吗
|
2月前
|
搜索推荐 前端开发 算法
基于用户画像及协同过滤算法的音乐推荐系统,采用Django框架、bootstrap前端,MySQL数据库
本文介绍了一个基于用户画像和协同过滤算法的音乐推荐系统,使用Django框架、Bootstrap前端和MySQL数据库构建,旨在为用户提供个性化的音乐推荐服务,提高推荐准确性和用户满意度。
110 7
基于用户画像及协同过滤算法的音乐推荐系统,采用Django框架、bootstrap前端,MySQL数据库
|
2月前
|
SQL 数据库连接 API
ThinkPHP6实现增删改查接口
ThinkPHP6实现增删改查接口
33 1
|
2月前
|
XML 数据库 数据格式
Spring5入门到实战------14、完全注解开发形式 ----JdbcTemplate操作数据库(增删改查、批量增删改)。具体代码+讲解 【终结篇】
这篇文章是Spring5框架的实战教程的终结篇,介绍了如何使用注解而非XML配置文件来实现JdbcTemplate的数据库操作,包括增删改查和批量操作,通过创建配置类来注入数据库连接池和JdbcTemplate对象,并展示了完全注解开发形式的项目结构和代码实现。
Spring5入门到实战------14、完全注解开发形式 ----JdbcTemplate操作数据库(增删改查、批量增删改)。具体代码+讲解 【终结篇】
|
2月前
|
SQL XML Java
Spring5入门到实战------12、使用JdbcTemplate操作数据库(增删改查)。具体代码+讲解 【上篇】
这篇文章是Spring5框架的实战教程,详细讲解了如何使用JdbcTemplate进行数据库的增删改查操作,包括在项目中引入依赖、配置数据库连接池、创建实体类、定义DAO接口及其实现,并提供了具体的代码示例和测试结果,最后还提供了完整的XML配置文件和测试代码。
Spring5入门到实战------12、使用JdbcTemplate操作数据库(增删改查)。具体代码+讲解 【上篇】
|
2月前
|
数据采集 前端开发 算法
基于朴素贝叶斯算法的新闻类型预测,django框架开发,前端bootstrap,有爬虫有数据库
本文介绍了一个基于Django框架和朴素贝叶斯算法开发的新闻类型预测系统,该系统具备用户登录注册、后台管理、数据展示、新闻分类分布分析、新闻数量排名和新闻标题预测等功能,旨在提高新闻处理效率和个性化推荐服务。
|
2月前
|
API 数据库 开发者
【独家揭秘】Django ORM高手秘籍:如何玩转数据模型与数据库交互的艺术?
【8月更文挑战第31天】本文通过具体示例详细介绍了Django ORM的使用方法,包括数据模型设计与数据库操作的最佳实践。从创建应用和定义模型开始,逐步演示了查询、创建、更新和删除数据的全过程,并展示了关联查询与过滤的技巧,帮助开发者更高效地利用Django ORM构建和维护Web应用。通过这些基础概念和实践技巧,读者可以更好地掌握Django ORM,提升开发效率。
23 0
下一篇
无影云桌面