十四 .Django 多对多表ManyToManyField (ORM)
一. 多对多表ManyToManyField (ORM)
https://www.cnblogs.com/yoyoketang/p/10580253.html 多对多(ManyToManyField)查询
https://www.cnblogs.com/yuanchenqi/articles/8963244.html 多表操作
1.创建orm表
多对多手动创建第三张关系表联
# 表一 class User(models.Model): username=models.CharField(max_length=24,db_index=True) def __str__(self): return self.username # 表二 class Tag(models.Model): title=models.CharField(max_length=24) def __str__(self): return self.title # 自定义第三张表 class UserToTag(models.Model): u=models.ForeignKey(to="User") t=models.ForeignKey(to="Tag") ctime=models.DateField()
多对多手动创建第三张关系表联合唯一
# 表一 class User(models.Model): username=models.CharField(max_length=24,db_index=True) def __str__(self): return self.username # 表二 class Tag(models.Model): title=models.CharField(max_length=24) def __str__(self): return self.title # m=models.ManyToManyField( # ManyToManyField d多对多 使用 ManyToManyField 只能在第三章表 中创建三列数 # to="User" , # 默认和user表的主键进行管理 # ) # 自定义第三张表 class UserToTag(models.Model): u=models.ForeignKey(to="User") t=models.ForeignKey(to="Tag") ctime=models.DateField() class Meta: # 表示联合唯一 和 ManyToManyField 差不多 unique_together=[ # 表达的意思就是标签和同一个人不能出现多次 就一个人对应一个标签 ("u","t"), ]
ManyToManyField自动创建第三表
class Colors(models.Model): colors=models.CharField(max_length=10) #蓝色 def __str__(self): return self.colors class Child(models.Model): name=models.CharField(max_length=10) #姓名 favor=models.ManyToManyField('Colors') #与颜色表为多对多
比如有多个孩子,和多种颜色、
每个孩子可以喜欢多种颜色,一种颜色可以被多个孩子喜欢,对于双向均是可以有多个选择
查数据
#多对多子表查询母表,查找小明喜欢哪些颜色--返回:[<Colors: 红>, <Colors: 黄>, <Colors: 蓝>] #与一对多子表查询母表的形式不同,因为一对多,查询的是母表的“一”;多对多,查询的是母表的“多” #写法1: child_obj=models.Child.objects.get(name="小明") #写法:子表对象.子表多对多字段.过滤条件(all()/filter()) print(child_obj.favor.all())
#写法2,反向从母表入手: print(models.Colors.objects.filter(child__name="小明")) #母表对象.filter(子表表名小写__子表字段名="过滤条件") #多对多母表查询子表,查找有哪些人喜欢黄色--返回:[<Child: 小明>, <Child: 丫蛋>] #与一对多母表查询子表的形式完全一致,因为查到的都是QuerySet,一对多和多对多,都是在查询子表的“多” #写法1: color_obj=models.Colors.objects.get(colors="黄") print(color_obj.child_set.all())
#写法2: print(models.Child.objects.filter(favor=models.Colors.objects.get(colors="黄")))
#写法2简便写法(推荐): print(models.Child.objects.filter(favor__colors="黄")) #写法:filter(子表外键字段__母表字段='过滤条件')
#写法3: color_id=models.Colors.objects.get(colors="黄").id #通过母表获取到颜色为红的id print(models.Child.objects.filter(favor=color_id)) #filter得到QuerySet,写法:filter(子表外键字段=母表主键对象),此处和一对多略有不同,是子表外键字段而不是外键字段_母表主键
增与改(增添子表或母表数据参照一对一的增,多对多重点在于关系表的对应关系变更)
#添加子表关联关系 #添加小虎并让他喜欢所有颜色 #写法1: child_obj=models.Child.objects.create(name="小虎") #如果是已有用户,使用.get() colors_obj=models.Colors.objects.all() #创建颜色表的所有颜色QuerySet对象 child_obj.favor.add(*colors_obj) #添加对应关系,将小虎和所有颜色进行关联,写法:子表对象.子表多对多字段.add(*QuerySet对象)
#写法2: child_obj=models.Child.objects.get(name="小虎") colors_obj=models.Colors.objects.all() child_obj.favor=colors_obj child_obj.save()
#让小虎喜欢黄色和蓝色(2种写法和上边一致,只展示一种写法) child_obj=models.Child.objects.get(name="小虎") colors_obj=models.Colors.objects.filter(colors__in=["蓝","黄"]) #models默认只能用这种方式得到并集,如需更复杂的过滤逻辑,需使用模块Q child_obj.favor.clear() #清空小虎已经喜欢的颜色 child_obj.favor.add(*colors_obj) #add是追加模式,如果当前小虎已经喜欢绿色,那么执行后,小虎会额外喜欢蓝,黄
#让小虎喜欢绿色(2种写法和上边一致,只展示一种写法) child_obj=models.Child.objects.get(name="小虎") colors_obj=models.Colors.objects.get(colors="绿") child_obj.favor.clear() child_obj.favor.add(colors_obj) #此处没有* #添加母表关联关系 #让喜欢蓝色的人里添加小虎,可以用上边的方法,一个效果,让小虎喜欢蓝色,下边介绍反向插入(从母表入手)的写法 child_obj=models.Child.objects.get(name="小虎") colors_obj=models.Colors.objects.get(colors="蓝") colors_obj.child_set.add(child_obj) #从colors表插入小虎,写法:母表对象.子表名小写_set.add(子表对象)。 让喜欢蓝色的child_set集合添加name="小虎"
#让所有人都喜欢蓝色 children_obj=models.Child.objects.all() colors_obj=models.Colors.objects.get(colors="蓝") colors_obj.child_set.add(*children_obj) #关于_set写法,是否已经有些晕了,究竟什么时候使用_set,简单记忆,只有子表才有"子表名小写_set"的写法,得到的是一个QuerySet集合,后边可以接.add(),.remove(),.update(),.delete(),.clear() #另外备注一下,colors_obj.child_set.clear()是让所有人喜欢的颜色里去掉蓝色,colors_obj.child_set.all().delete()是删除.child_set的所有人
删:删除多对多表关系 :
#删除子表与母表关联关系 #让小虎不喜欢任何颜色 #写法1: child_obj=models.Child.objects.get(name="小虎") colors_obj=models.Colors.objects.all() child_obj.favor='' child_obj.save()
#写法2: child_obj=models.Child.objects.get(name="小虎") colors_obj=models.Colors.objects.all() child_obj.favor.remove(*colors_obj)
#写法3: child_obj=models.Child.objects.get(name="小虎") child_obj.favor.clear() #其他例子参照多对多的增与改案例,这里不做举例 #删除母表与子表关联关系 #让所有人不再喜欢蓝色 #写法1: children_obj=models.Child.objects.all() colors_obj=models.Colors.objects.get(colors="蓝") colors_obj.child_set.remove(*children_obj) #写法2: colors_obj=models.Colors.objects.get(colors="蓝") colors_obj.child_set.clear()
删除多对多表数据:
#删除子表数据 #喜欢蓝色的所有人都删掉 colors_obj=models.Colors.objects.get(colors="蓝") colors_obj.child_set.all().delete() #注意有.all()
#删除所有child models.Child.objects.all().delete()
删除母表数据:
默认情况下,如此例中,删除“红”色,那么子表与颜色表是一对一或外键关系的,子表对应数据会自动删除,如:红球,小虎哥
与颜色表是多对多关系的话,不会自动删除喜欢红色的人,而是去掉红色已选
如果想让与母表外键关联的子表在删除外键之后依旧可以保留子表数据,需要子表建表时加入以下字段:
class Clothes(models.Model): color=models.ForeignKey("Colors",null=True,on_delete=models.SET_NULL)) #可为空,如果外键被删后,子表数据此字段置空而不是直接删除这条数据,同理也可以SET_DEFAULT,需要此字段有默认值 description=models.CharField(max_length=10) #描述
choice
#choices相当于实现一个简化版的外键,外键的选项不能动态更新,如可选项目较少,可以采用 #先在models添加choices字段 class Child(models.Model): sex_choice=((0,"男"),(1,"女")) name=models.CharField(max_length=10) #姓名 favor=models.ManyToManyField('Colors') #与颜色表为多对多 sex=models.IntegerField(choices=sex_choice,default=0) def __unicode__(self): return self.name #在views.py中调用 child_obj=models.Child.objects.get(name="小虎") print(child_obj.sex) #返回0或1 print(child_obj.get_sex_display()) #返回男或
多对对案例2
# models.py form django.db import models class Book(models.Model): # 表名book,django会自动使用项目名+我们定义的表名 # 如没有自定义主键,django会自动添加一个主键,字段名id 自增 name = models.CharField(max_length=20) # 字段名name 类型 vachar(20) price = models.IntegerField() # 字段名price 类型int pub_date = models.DateField() # 字段名pub_date 类型 date (时间戳) publish = models.ForeighKey('Publish') # 创建外键关联到Publish表的id字段,django会自动将该名称改为publish_id # 如果这样写 publish = models.ForeighKey(Publish) 括号内无引号,则必须将Publish类放到Book类的上面 authors = models.ManyToManyField('Author',related_name='xxx') 将book与author表做多对多关系 related_name 同一对多中的说明 # django会自动创建一张表(book与author的中间关联表)名称为appname_book_authors def __str__(self): return self.name # 打印实例对象时显示为self.name #class Book_Author(models.Model): 自己创建第三张表 # book = models.ForeignKey('book') # author = models.ForeignKey('Author') class Publish(models.Model): name = models.CharField(max_length=32) city = models.CharField(max_length=32) class Author(models.Model): name = models.CharField(max_length=32) age = models.IntegerField() def __str__(self): return self.name
多对多的添加设置删除
# views.py from django.shortcuts import render from app_name.models import * # 导入models.py def add(request): # 增加数据的视图函数 # 系统自己创建的第三张表使用创建对象操作 book_obj = Book.objects.get(id=4) # 取出id为4的书 # book_obj.authors.all() 此时取出的是一个空的集合 authors_obj = Author.objects.all() # 取出所有author名称的集合 book_obj.authors.add(*author_obj) # 将所有作者添加到这本书中 book_obj.authors.remove(*author_obj) # 将所有作者从书中删除 book_obj.authors.add(2) # 将id为2的作者添加到此书 book.obj.authors.add([1,2]) # 将id为1和2的作者添加到此书 book_obj.authors.remove(1) # 将id为1的作者从书中删除 book_obj.authors.clear() # 清除此书所有的作者 book_obj.authors.set([2,3,4]) # 将书的作者设置为id为2,3,4的作者 (相当于重新设置) # 我们自己定义的第三张表(不常用) Book_Author.objects.create(book_id=2, author_id=3) obj = Book.objects.get(id=2) obj.book_author_set.all()[0]l.author return HttpResponse('xxx')
多对多的查询
# 怎么使用多对多查询呢? book_obj = Book.objects.get(name='python') print(book_obj.name) # python print(book_obj.authors.all()) # QuerySet 返回一个QuerySet对象,里面是author的实例集合 print(type(book_obj.authors.all())) # <class 'django.db.models.query.QuerySet'> 1、查询作者id为2出的所有的书 author_obj=Author.objects.get(id=2) author_obj.book_set.all() 2、查询三班所对应的所有老师 obj = Classes.objects.filter(name='三班').first() 从班级表取出三班 obj.m.all() # 从三班中取所有的老师 m表示多对多关系的名称 还是通过双下划线 2、查询作者alex出的所有书 Book.objects.filter(authors__name='alex').values('name','price')