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

目录
相关文章
|
9天前
|
关系型数据库 MySQL 数据库
|
9天前
|
SQL 关系型数据库 MySQL
MySQL | 数据库的管理和操作【表的增删改查】(二)
MySQL | 数据库的管理和操作【表的增删改查】(二)
|
1天前
|
数据采集 自然语言处理 Python
在 Django 中设计爬虫系统的数据模型与多对多关系
在构建爬虫系统时,设计合理的数据模型和多对多关系对系统的性能和可维护性至关重要。本文将探讨如何使用 Django 来设计爬虫系统的数据模型。
|
9天前
|
前端开发 数据库 Python
使用 Python 的 Web 框架(如 Django 或 Flask)来建立后端接口,用于处理用户的请求,从数据库中查找答案并返回给前端界面
【1月更文挑战第13天】使用 Python 的 Web 框架(如 Django 或 Flask)来建立后端接口,用于处理用户的请求,从数据库中查找答案并返回给前端界面
101 7
|
9天前
|
API 数据库 Python
Python web框架fastapi数据库操作ORM(二)增删改查逻辑实现方法
Python web框架fastapi数据库操作ORM(二)增删改查逻辑实现方法
|
9天前
|
JavaScript 前端开发 API
node+vue3+mysql前后分离开发范式——实现对数据库表的增删改查
node+vue3+mysql前后分离开发范式——实现对数据库表的增删改查
38 1
|
9天前
|
Python
使用Django时,如何设计模型关系(一对一、一对多、多对多)?
Django支持三种模型关联:ForeignKey(一对多),OneToOneField(一对一)和ManyToManyField(多对多)。ForeignKey示例:`Article`有一个指向`Author`的外键。OneToOneField示例:`UserProfile`与`User`一对一关联。ManyToManyField示例:`Student`和`Course`之间多对多关系。这些关联字段便于反向查询,如`article.author`获取作者,`author.article_set.all()`获取作者所有文章。
17 1
|
9天前
|
SQL 缓存 数据库
在Python Web开发过程中:数据库与缓存,如何使用ORM(例如Django ORM)执行查询并优化查询性能?
在Python Web开发中,使用ORM如Django ORM能简化数据库操作。为了优化查询性能,可以:选择合适索引,避免N+1查询(利用`select_related`和`prefetch_related`),批量读取数据(`iterator()`),使用缓存,分页查询,适时使用原生SQL,优化数据库配置,定期优化数据库并监控性能。这些策略能提升响应速度和用户体验。
21 0
|
9天前
|
安全 数据库连接 数据库
Flask数据库操作实战:增删改查一网打尽
【4月更文挑战第15天】本文介绍了在Flask中进行数据库操作的方法,包括选择数据库扩展(如Flask-SQLAlchemy)、配置数据库、定义模型以及执行CRUD操作。通过Flask-SQLAlchemy的ORM功能,开发者可以方便地管理数据库表和记录。文章详细展示了如何创建模型、添加、查询、更新和删除数据,并提到了高级查询和关系映射。此外,还提及了数据库迁移工具Flask-Migrate以及性能优化和安全性问题。了解这些基础,有助于开发者构建高效、安全的Flask Web应用。
|
9天前
|
关系型数据库 API 数据库
Django中的数据库事务管理:确保数据一致性
【4月更文挑战第15天】Django框架提供强大的数据库事务管理,确保ACID属性,保证数据一致性和完整性。文章深入讨论了Django事务管理,包括使用`@transaction.atomic`装饰器和`transaction.atomic()`上下文管理器手动控制事务,以及低级API进行精细管理。注意避免长时间事务、选择合适隔离级别、正确处理异常及了解数据库特性。掌握这些技巧对构建可靠Web应用至关重要。