ORM关联表模型
一、创建表
1.一对一:
1 class Author(models.Model):2 nid = models.AutoField(primary_key=True)3 name=models.CharField( max_length=32)4 age=models.IntegerField()5 6 # 与AuthorDetail建立一对一的关系,外键随便在哪一方7 authorDetail=models.OneToOneField(to="AuthorDetail",on_delete=models.CASCADE) #生成一对一或一对多的外键时必须加on_delete=models.CASCADE8 #注意!!!必写:on_delete=models.CASCADE:删除关联数据,与之关联也删除
2.一对多:
1 class Book(models.Model):2 3 nid = models.AutoField(primary_key=True)4 title = models.CharField( max_length=32)5 publishDate=models.DateField()6 price=models.DecimalField(max_digits=5,decimal_places=2)7 8 # 与Publish建立一对多的关系,外键字段建立在多的一方(多的一方对应少的一个)9 publish=models.ForeignKey(to="Publish",to_field="nid",on_delete=models.CASCADE)
3.多对多:
1 class Book(models.Model): 2 3 nid = models.AutoField(primary_key=True) 4 title = models.CharField( max_length=32) 5 publishDate=models.DateField() 6 price=models.DecimalField(max_digits=5,decimal_places=2) 7 8 9 # 与Author表建立多对多的关系,ManyToManyField可以建在两个模型中的任意一个,自动创建第三张表10 authors=models.ManyToManyField(to='Author',)
二、添加表数据:
1.一对多:
1 #单表添加记录(没有任何关联) 2 pub = Publish.objects.create(name="人民出版社", city="北京", email="123@qq.com") 3 4 #为book表绑定关系:publish 5 #方式一: 6 book_obj = Book.objects.create(title="红楼梦", publishDate="2018-05-06", price="200", publish_id=1) 7 print(book_obj.title) # 返回值book_obj为插入的这条数据的对象 8 # 方式二: 9 publish = Publish.objects.filter(nid=1).first() # 拿到nid=1的queryset,只有加了first才在列表中拿到第一个对象10 book_obj = Book.objects.create(title="水浒传", publishDate="2522-01-06", price="500", publish=publish)11 print(book_obj.title) # 返回值book_obj为插入的这条数据的对象12 print(book_obj.publish) # 与这本书关联的出版社的对象:Publish object (1)13 print(book_obj.publish_id) # 打印publish_id14 print(book_obj.publish.name)15 print(book_obj.publish.city)16 17 return HttpResponse("OK")
2.多对多:
1 '''-----------------------------绑定多对多的关系-------------------------------''' 2 3 #创建数据 4 book_obj = Book.objects.create(title="金2", publishDate="1920-11-22", price=50, publish_id=2) 5 6 # 为书籍绑定的做作者对象 7 alex=Author.objects.filter(name="alex").first() # 在Author表中主键为2的纪录 8 egon=Author.objects.filter(name="egon").first() # 在Author表中主键为1的纪录 9 10 11 # 绑定多对多关系,即向关系表book_authors中添加纪录12 #pycharm提供的接口找到多对多关系的表,book_obj(添加数据的对象).authors(在创建表类的时候ManytoManyField开的接口).add()13 book_obj.authors.add(alex, egon) # 将某些特定的 model 对象添加到被关联对象集合中。 ======= book_obj.authors.add(*[])14 book_obj.authors.add(1,2) # 可以直接添加主键15 book_obj.authors.add(*[1,2,3]) # 给函数添加列表要在前面加*
3.解除多对多关系:
1 #解除多对多的关系2 #解除的前提:先查到一个数据,找对应数据解除3 book_obj = Book.objects.filter(nid=9).first()4 # book_obj.authors.remove(2) # book_obj=9,authors=2,解除关系9-25 #6 # book_obj.authors.clear() # 清空被关联对象集合,只要book_id=4全部清除7 8 print(book_obj.authors.all()) #类型:queryset 获取所有book_obj关联的作者对象的集合9 print(book_obj.authors.all().value("name")) # 拿到author的名字
三、跨表查询
1.基于对象的查询------->子查询
一对多:
1 A-B 2 3 正向查询:关联属性在A表中 A--->B 4 反向查询:关联属性在A表中 B--->A 5 6 7 一对多查询: 8 正向查询按字段 9 反向查询安表名小写_set.all()10 11 (正)book_obj.publish12 Book(关联属性:publish) --------------> Publish13 (反)book_set.all() #queryset
实例:
(1)一对多正向查询:
1 #一对多正向查询:查询金这本书的出版社名字2 3 book_obj = Book.objects.filter(title="金").first()4 print(book_obj.publish.name) # 与这本书关联的出版社对象,的名字
(2)一对多反向查询:
1 #一对多反向查询:查询人民出版社出版过的书籍名称2 publish = Publish.objects.filter(name="人民出版社").first()3 print(publish.book_set.all()) # 拿到书籍对象的集合queryset
多对多:
1 多对多查询:2 正向查询按字段.all()3 反向查询安表名小写_set.all()4 5 (正)book_obj.authors.all()6 Book(关联属性:publish) ------------------------> Author7 (反)book_set.all() #queryset
实例:
(1)多对多正向查询:
1 #多对多正向查询:查询金这本书的所有作者名字2 book_obj = Book.objects.filter(title="金").first()3 author_list = book_obj.authors.all() # queryset4 5 for author in author_list: # 循环queryset拿到每一个object6 print(author.name) # 调用对象下的属性
(2)多对多反向查询:
1 # 多对多反向查询:查询alex出版过的所有书籍名称2 author_obj = Author.objects.filter(name="alex").first()3 book_list = author_obj.book_set.all()4 5 for book in book_list:6 print(book.title)
一对一:
1 一对一查询:2 正向查询按字段3 反向查询按表名4 5 (正)author_obj.authordetail6 Author(关联属性:authorDetail) ------------------------> AuthorDetail7 (反)authordetail_obj.author
实例:
(1)一对一正向查询:
1 # 一对一正向查询:查询alex的手机号2 alex = Author.objects.filter(name="alex").first()3 print(alex.authordetail.telephone)
(2)一对一反向查询:
1 # 一对一反向查询:查询手机号为110的作者名2 phone = AuthorDetail.objects.filter(telephone=110).first()3 print(phone.author.name)
2.基于双下划线查询:
步骤:
00.记住格式:表名.object.filter(表名小写="").value("")
01.找到问题的切入点,找到一个表的对象当主对象,例如:Book.object
02.看主对象是否有能够直接找到的本表数据,如果有,则在filter中获取,如果则在value里查找
03.用__进行跨表操作
案例:
1 #一对多查询:查询金这本书的出版社名字 2 #方式一 3 ret = Book.objects.filter(title="金").values("publish__name") 4 print(ret) #5 6 #方式二 7 ret = Publish.objects.filter(book__title="金").values("name") 8 print(ret) 9 10 11 #多对多查询:查询金这本书的所有作者名字12 13 #方式一:14 ret = Book.objects.filter(title="金").values("authors__name")15 print(ret) # 16 17 18 #方式二:19 ret = Author.objects.filter(book__title="金").values("name")20 print(ret) # 21 22 23 24 #一对一查询:查询alex手机号25 ret = Author.objects.filter(name="alex").values("authordetail__telephone")26 print(ret)
3.基于下划线的跨表查询(连续跨表)
实例:
1 #手机号以110开头的作者出版过的所有书籍名称以及书籍出版社名称2 #方法一3 ret = Book.objects.filter(authors__authordetail__telephone__startswith="110").values("title","publish__name")4 print(ret)5 6 #方法二:7 ret = Author.objects.filter(authordetail__telephone__startswith="110").values("book__title","book__publish__name")8 print(ret)
4.聚合查询
(1)聚合(aggregate) 返回值:字典,而不是queryset
实例:
1 #查询所有书籍的平均价格2 from django.db.models import Avg, Max, Min, Sum, Count3 ret = Book.objects.all().aggregate(Avg("price"), Max("price")) # 可设置字典的key:avg_price=("属性")_4 print(ret) # {'price__avg': 205.555556}
(2)分组查询(annotate) 返回值:queryset
实例:
1 #单表分组查询 2 3 #查询每一个出版社所在成熟的个数 4 ret = Publish.objects.values("name").annotate(Count("city")) 5 print(ret) # 一个queryset的字典: 6 #7 ''' 8 单表分组查询的ORM语法:单表模型.objects.values("group by 的字段").annotate(聚合函数("统计字段")) 9 10 补充知识:11 ret = Publish.objects.all() =====翻译成sql语句===== select * from publish12 只要包含主键的分组,那么这个分组都毫无意义13 ret = Publish.objects.values("name") =====翻译成SQL语句===== select name from publish14 15 16 '''17 18 19 #多表分组查询20 21 #查询每一个出版社出版的书籍几个数22 Book.objects.values("publish_id").annotate(c=Count("nid"))23 24 #案例一:查询每一个出版社的名称以及出版的数几个数25 #方式一:26 Publish.objects.values("name").annotate(Count("book__title"))27 #方式二:用表的主键id进行分组,再在分组后,整个关联的表中再进行values的select操作28 Publish.objects.values("nid").annotate(c = Count("book__title")).values("name","c")29 30 #案例二:查询每一个作者价格最高的书籍31 ret = Author.objects.values("pk").annotate(max_price=Max("book_price")).values("name","max_price") # pk为该表模型的主键32 33 #案例三:查询每一个书籍的名称以及对应的作者个数34 35 ret = Book.objects.values("pk").annotate(c = Count("authors__name")).values("title","c")36 37 #案例四:统计不止一个作者的图书38 ret = Book.objects.values("pk").annotate(c=Count("author__name")).filter(c__gt=1).values("title")39 print(ret)
分组查询总结:
1 跨表的分组查询模型:三套模型效果一样2 01.“每一个”字段后表的模型.objects.values("pk").annotate(聚合函数("关联表__统计字段")).values("表模型的所有字段以及统计字段")3 02.“每一个”字段后表的模型.objects.all().annotate(聚合函数("关联表__统计字段")).values("表模型的所有字段以及统计字段")4 03.“每一个”字段后表的模型.objects.annotate(聚合函数("关联表__统计字段")).values("表模型的所有字段以及统计字段")
5.F查询与Q查询
(1)F查询:
应用场景:之前构造的过滤器filter都只是将字段值与某个常量做比较。如果我们要对两个字段的值做比较
作用:F() 的实例可以在查询中引用字段,来比较同一个 model 实例中两个不同字段的值
前提:from django.db.models import F
实例:
1 # 查询评论数大于收藏数的书籍 2 3 from django.db.models import F 4 Book.objects.filter(commnetNum__lt=F('keepNum')) 5 6 # Django 支持 F() 对象之间以及 F() 对象和常数之间的加减乘除和取模的操作。 7 8 # 查询评论数大于收藏数2倍的书籍 9 Book.objects.filter(commnetNum__lt=F('keepNum')*2)10 11 # 修改操作也可以使用F函数,比如将每一本书的价格提高30元:12 Book.objects.all().update(price=F("price")+30)
(2)Q查询:
应用场景:filter() 等方法中的关键字参数查询都是一起进行“AND” 的。
作用:如果你需要执行更复杂的查询(例如OR 语句),你可以使用Q 对象
前提:from django.db.models import Q
实例:
1 Q 对象可以使用& 和| 操作符组合起来。当一个操作符在两个Q 对象上使用时,它产生一个新的Q 对象。 2 3 bookList=Book.objects.filter(Q(authors__name="yuan")|Q(authors__name="egon")) 4 等同于下面的SQL WHERE 子句: 5 6 WHERE name ="yuan" OR name ="egon" 7 你可以组合& 和| 操作符以及使用括号进行分组来编写任意复杂的Q 对象。同时,Q 对象可以使用~ 操作符取反,这允许组合正常的查询和取反(NOT) 查询: 8 9 bookList=Book.objects.filter(Q(authors__name="yuan") & ~Q(publishDate__year=2017)).values_list("title")10 查询函数可以混合使用Q 对象和关键字参数。所有提供给查询函数的参数(关键字参数或Q 对象)都将"AND”在一起。但是,如果出现Q 对象,它必须位于所有关键字参数的前面。例如:11 12 bookList=Book.objects.filter(Q(publishDate__year=2016) | Q(publishDate__year=2017),title__icontains="python")
注意!!!为已经创建的表增加新的字段需要设置default=0,例如:
1 read_num = models.IntegerField(default=0)2 comment_num = models.IntegerField(default=0)