Django模型 - 多表操作
一 创建模型
作者模型:一个作者有姓名和年龄。
作者详细模型:把作者的详情放到详情表,包含生日,手机号,家庭住址等信息。作者详情模型和作者模型之间是一对一的关系(one-to-one)
出版社模型:出版社有名称,所在城市以及email。
书籍模型: 书籍有书名和出版日期,一本书可能会有多个作者,一个作者也可以写多本书,所以作者和书籍的关系就是多对多的关联关系(many-to-many);一本书只应该由一个出版商出版,所以出版商和书籍是一对多关联关系(one-to-many)。
生成表如下:
二 多表添加
一对一新增 (可以传对象或id)
先创建没有外键的数据,然后类的字段等于对象(方式一)
authordetail = AuthorDetail.objects.create(phone='19856482547',email='4878779@qq.com') author = Author.objects.create(name='张三',age=28,authordetail=authordetail)
直接知道姓名给authordetail_id 赋值 (方式二)
author = Author.objects.create(name='安妮',age=12,authordetail_id=7) print(author)
一对多增加
方式一 (同上)
publish = Publish.objects.create(name='北京出版社',addr='北京西路',email='887@163.com') book = Book.objects.create(name='花非花',price=34,pub_date='1972-9-4',publish=publish)
方式二
publish = Publish.objects.filter(name='北京出版社').first() book = Book.objects.create(name='雾非雾',price=36,pub_date='1972-9-4',publish_id=publish.nid)
多对多增加
book = Book.objects.create(name='西游记',price=78,pub_date='2017-9-2',publish_id=3) # 多对多,添加关系add ,传对应的(作者nid)id book.author.add(1,2) # 没有返回值 # ret = book.author.add(3,6) # ret = book.author.add(*(3,6)) #add里面可以传对象,也可以传多个,以逗号分隔,也可以*(作者对象,作者对象) book = Book.objects.create(name='FOX',price=34,pub_date='2017-7-19',publish_id=3) author = Author.objects.filter(pk=7).first() book.author.add(author)
多对多关系解除绑定
remove
解除绑定关系(既可以传对象 又可以传author_id,既可以传多个又可以传一个) 移除一条或多条
book = Book.objects.filter(pk=8).first() # ret = book.author.remove(author.id) ret = book.author.remove(7) author = Author.objects.filter(pk=6).first() ret = book.author.remove(author) # ret = book.author.remove(2,5) # ret = book.author.remove(*(2,5))
clear 一次性解除全部绑定关系
book = Book.objects.filter(pk=8).first()
book.author.clear()
set 参数必须传入可迭代对象,可以传id,也可以传对象
book = Book.objects.filter(pk=6).first() author = Author.objects.filter(pk=3).first() book.author.set([author]) #先执行clear,再执行add 先清空在添加
三 基于对象正反向查询
一对一
查询安妮的电话号码(正向查询 按字段) # anni = Author.objects.filter(name='安妮').first() # print(anni.authordetail.phone,type(anni.authordetail)) 查询电话号码是 13956485512 的作者(反向查 按表名小写) # authordetail = AuthorDetail.objects.filter(phone='13956485512').first() # print(authordetail.author) # print(authordetail.author.name)
一对多
#查询红楼梦是哪个出版社 # 正向查 # 反向查 ''' A表book(关联字段) B表 publish 正向查询 A--->B 关联字段在A,A去查询B表,这叫正向查询,按字段来查 反向查询 B--->A 关联字段在A,B去查询A表,这叫正向查询,按表名小写_set ''' # 正向查询 # book = Book.objects.filter(name='红楼梦').first() # print(book.publish.name) # 反向查询 # 北京出版社所有书 # publish = Publish.objects.filter(name='北京出版社').first() # print(publish.book_set.all())
多对多
#查询红楼梦这本书所有作者(正向 按字段) # book = Book.objects.filter(name='红楼梦').first() # print(book.author.all()) #查询张三出的所有书( 反向查 按表名小写_set) # zs = Author.objects.filter(name='张三').first() # print(zs.book_set.all()) ''' A表book(关联字段) B表 publish 正向查 A -->B 反向查 B -->A 总结: 正向查询按字段 反向查询按表名小写告诉ORM引擎join那张表 一对一 正向: 按字段 反向: 按表名小写 一对多 正向: 按字段 反向: 按表名小写_set 多对多 正向: 按字段 反向: 按表名小写_set '''
四 基于双下滑线的多表查询(连表查询)
一对多查询
查询北京出版社出版过的所有书籍价格、名字(反向 ) # ret = Publish.objects.filter(name='北京出版社').values('book__price','book__name') # print(ret) 正向 # res = Book.objects.filter(publish__name='北京出版社').values('name','price') # print(res)
多对多查询
#查询王五出过的所有书籍名字、价格(多对多) #正向 # ret = Book.objects.filter(author__name='王五').values('name','author__name','author__authordetail__phone') # ret = Book.objects.filter(author__name='王五').values('name','price') # print(ret) #反向 # ret = Author.objects.filter(name='王五').values('book__name','book__price') # print(ret)
一对一
查询王五的手机号(正向) # ret = Author.objects.filter(name='王五').values('authordetail__phone') # print(ret) #反向 按表名小写 # ret = AuthorDetail.objects.filter(author__name='王五').values('phone') # print(ret)
''' 总结: 用__告诉ORM ,要连接哪个表 一对一 正向: 按字段 反向:按表名小写 一对多 正向: 按字段 反向:按表名小写 多对多 正向: 按字段 反向:按表名小写 ''' # 手机号以13开头的作者出过的所有书籍名称以出版社名称 # ret = Book.objects.filter(author__authordetail__phone__startswith='13').values('name','publish__name') # print(ret) # ret = AuthorDetail.objects.filter(phone__startswith='13').values('author__book__name','author__book__publish__name') # print(ret) # ret = Author.objects.filter(authordetail__phone__startswith='13').values('book__name','book__publish__name') # print(ret) # ret = Publish.objects.filter(book__author__authordetail__phone__startswith='13').values('name','book__name') # print(ret)
五 聚合查询和分组查询
聚合
聚合 aggregete # 计算所有图书的平均价格 from django.db.models import Avg,Max,Min,Count,Sum # ret = Book.objects.all().aggregate(c=Avg('price')) # print(ret) # 计算所有图书总价 # ret = Book.objects.all().aggregate(s=Sum('price')) # print(ret) # 最大价格 # ret = Book.objects.all().aggregate(m=Max('price')) # print(ret) # 所有图书价格最大值个最小值,返回结果是字典 # ret = Book.objects.all().aggregate(b_max=Max('price'),b_min=Min('price')) # print(ret)
分组
annotate()为调用queryset中每一个对象都生成独立的统计值 #跨表分组查询本质就是将关联表join成一张表,再按单表的思路进行分组 from django.db.models import Avg,Max,Sum,Min,Count # 统计每本书的作者数 # book_list = Book.objects.all().annotate(author_num=Count('author','author')) # for book in book_list: # print(book.name,':',book.author_num) #同上 values 在这里指group_by 的字段 ret = Book.objects.values('name').annotate(author_num=Count('author__name')).values('name','author_num') print(ret) # book_list = Book.objects.all().annotate(author_num=Count('author')).values('name','author_num') # print(book_list) # 统计每一出版社的最便宜的书 # ret = Publish.objects.all().annotate(p=Min('book__price')).values('name','p') # print(ret) # 统计每一本以 红 开头的书籍的作者个数 # ret = Book.objects.all().filter(name__startswith='红').annotate(num=Count('author')).values('name','num') # print(ret) # ret = Book.objects.filter(name__startswith='西').annotate(num=Count('author')).values('name','num') # print(ret) # 统计不止一个作者的图书:(作者数量大于1) # ****注意values在annotate前,代表group by的字段,不写values 默认以基表的主键做group by 在后代我要select出来字段 # ret = Book.objects.all().values('name').annotate(num=Count('author__name')).filter(num__gt=1).values('name','num') # print(ret) # ret = Book.objects.all().annotate(num=Count('author')).filter(num__gt=1).values('name','num') # print(ret) # 根据一本图书作者数量的多少对查询集queryset进行排序 # ret = Book.objects.annotate(num=Count('author')).order_by('num').values('name','num') # print(ret) # 查询各个作者出的书的总价格 # ret = Author.objects.all().annotate(p=Sum('book__price')).values('name','p') # print(ret) #查询每个出版社的名称和书籍个数 # ret = Publish.objects.annotate(num=Count('book')).values('name','num') # print(ret)
六 F查询和 Q查询
F查询
F() 的实例可以在查询中引用字段,来比较同一个 model 实例中两个不同字段的值。
# 查询评论数大于收藏数的书籍 from django.db.models import F Book.objects.filter(commnetNum__lt=F('keepNum'))
Django 支持 F() 对象之间以及 F() 对象和常数之间的加减乘除和取模的操作。
# 查询评论数大于收藏数2倍的书籍 Book.objects.filter(commnetNum__lt=F('keepNum')*2)
修改操作也可以使用F函数,比如将每一本书的价格提高30元:
Book.objects.all().update(price=F("price")+30)
Q查询
filter() 等方法中的关键字参数查询都是一起进行“AND” 的。 如果你需要执行更复杂的查询(例如OR 语句),你可以使用Q 对象。
名字叫花非花 或者price是39的数 | 或 & 和 ~ 非 # ret = Book.objects.all().filter(Q(name='花非花')|Q(price='39'))
你可以组合& 和| 操作符以及使用括号进行分组来编写任意复杂的Q 对象。同时,Q 对象可以使用~ 操作符取反,这允许组合正常的查询和取反(NOT) 查询:
查询函数可以混合使用Q 对象和关键字参数。所有提供给查询函数的参数(关键字参数或Q 对象)都将"AND”在一起。但是,如果出现Q 对象,它必须位于所有关键字参数的前面。例如:
bookList=Book.objects.filter(Q(publishDate__year=2016) | Q(publishDate__year=2017), title__icontains="python")