django模型层之多表关系
一. 多表操作
数据库表关系之关联字段与外键约束
一对多
book(多)
publish(一)
查询<<水浒传>>这本书出版社的地址:
select publish_id from Book where title="水浒传" select addr from Publish where id=1
一旦确定了一对多的关系: 建立一对多的关系,在多的一方建立关联字段(publish_id)
多对多:
book
author
book2author:
panshao出版过得书籍名称(子查询)
select id from author where name='panshao'; # 在作者表中拿出panshao select book_id from book2author where author_id=1; # 找出作者id等于1的书籍的id select title from book where id=book_id;
一旦确定表关系是多对对: 创建第三张表 id book_id author_id
一对一:
author
authordetail
一旦确定是一对多的关系: 建立一对多的关系=======>在多的表中建立关联字段 (一个出版社可以出版多本书)
一旦确定为多对多的关系: 建立多对多的关系========> 创建第三张表(关联表): id和两个关联字段(一本书可以有多个作者,一个作者可以出版多本书)
一旦确定一对一关系: 建立一对一的关系========>在两张表中任意一张表中建立关联字段+unique(一个作者只有一个详细信息)
数据库表关系之创建关联表
from django.db import models class Book(models.Model): title = models.CharField(max_length=32) price = models.DecimalField(max_digits=8, decimal_places=2) publish_time = models.DateField(auto_now_add=True) # 外键关系 publish = models.ForeignKey(to='Publish') # 默认关联的是主键,如果相关联其他字段则可以使用to_field= authors = models.ManyToManyField(to='Author') # 告诉数据库让建一个多对多的表,在这里只是标识 class Publish(models.Model): name = models.CharField(max_length=32) addr = models.CharField(max_length=32) # email = models.EmailField() class Author(models.Model): name = models.CharField(max_length=32) age = models.IntegerField() authordetail = models.OneToOneField(to='AuthorDetail') class AuthorDetail(models.Model): phone = models.CharField(max_length=32) addr = models.CharField(max_length=32)
注意事项:
1. id字段是自动添加的
2. 对于外键字段, Django会在字段上自动添加"_id"来创建数据库中的列名
3. 外键字段foreignkey有一个null=True 的设置(他允许外检接受空值NULL), 你可以赋给他空值None
一对一就是OnetoOneField;一对多就是ForeignKey;他俩是一样的,会在那张表中生成一个字段,加一个_id+一个约束关系;多对多就是ManyToManyField,生成一个第三张关系表
2. 多表操作
一对多增删改查
# book_obj = models.Book.objects.create(title='都是东游记',price=108, publish_id=1 ) print(book_obj.title) # 返回值book_obj就是添加记录的对象 方式二: publish_obj= models.Publish.objects.filter(id=2).first() # 查询id为2的出版社对象;不加first就是queryset的那个集合 book_obj = models.Book.objects.create(title='西游记', price=345.45, publish=publish_obj) # publish就是Book表里面的那个字段 # 让他等于publish的一个实例模型对象,赋值给publish_obj给了他一个对象,相当于把方式一的方法给翻译过来了
改:
book_obj = models.Book.objects.filter(pk=1).update(publish_id=3) # 拿出book_id等于1 的书籍把他的出版社id改为其他的出版社 方式二: 传对象 publish_obj = models.Publish.objects.filter(pk=4).first() # 拿出id等于4 的出版社 models.Book.object.filter(pk=1).update(publish=publish_obj) # 将id等于1 的书籍的出版社改为id等于4 的出版社id
删除:
models.Book.objects.filter(pk=1).delete() # 将id为1的书籍进行删除 models.Publish.objects.filter(pk=1).delete() # 将publish_id为1的书籍进行删除
多对多的增删改查
# 该主键为1的书籍添加两个作者 # 思路: 先拿出主键唯一的书籍 book_obj = models.Book.objects.filter(pk=15).first() print(book_obj.authors) # 对象点击多对多虚拟字段,会直接跨到多对多的第三张表 book_obj.authors.add(2,3) # 将id等于1,3 的作者添加至第三张表中 方式二: author_obj = models.Author.objects.filter(pk=15).first() book_obj.authors.add(author_obj)
add给书籍添加作者,括号内既可以传数字也可以传对象,并且可以支持一次性传多个,用逗号隔开就行
改:
book_obj = models.Book.objects.filter(pk=15).first() # 拿出id等于15的书籍 book_obj.authors.set([2,3]) # 将id等于15的书籍作者设置为2, 3 # 将主键为15的书籍改为作者为2, 3 book_obj = models.Book.objects.filter(pk=15).first() # book_obj.authors.set([2,3]) author_obj = models.Author.objects.filter(pk=3).first() book_obj.authors.set([author_obj,]) # 自动将author_obj的id值直接传进来
set()括号内,需要传一个可迭代对象,可迭代对象中可以是多个数字的组合,也可以是多个对象组合,但是不要混着用!!!
删除:
book_obj = models.Book.objects.filter(pk=15).first() book_obj.authors.remove(2) # 删除作者为2的那条数据 author_obj = models.Author.objects.filter(pk=1).first() # author_obj1 = models.Author.objects.filter(pk=2).first() # author_obj2 = models.Author.objects.filter(pk=3).first() # book_obj.authors.remove(author_obj) # id等于1的作者删除 # book_obj.authors.remove(author_obj1,author_obj2)
remove()括号内既可以传数字,也可以传对象,并且支持传多个,逗号隔开
将某本书跟作者的关系全部清空 book_obj = models.Book.objects.filter(pk=1).first() book_obj.authors.clear() # 清空当前书籍与作者的所有关系
3. 跨表查询
正向与反向的概念
正向: 外键字段在当前查询的对象里面,由这个对象查另外一张表,就是正向查询
反向: 关系字段不在当前对象中,当前字段没有标识
# 一对一 # 正向:author---关联字段在author表里--->authordetail 按字段 # 反向:authordetail---关联字段在author表里--->author 按表名小写 # 一对多 # 正向:book---关联字段在book表里--->publish 按字段 # 反向:publish---关联字段在book表里--->book 按表名小写_set.all() 因为一个出版社对应着多个图书 # 多对多 # 正向:book---关联字段在book表里--->author 按字段 # 反向:author---关联字段在book表里--->book 按表名小写_set.all() 因为一个作者对应着多个图书 正向查询按外键字段 反向查询按表名小写 """ """基于对象的跨表查询(子查询:将一张表的查询结果当做另外一个查询语句的条件)"
基于对象的跨表查询(子查询:将一张表的查询结果当做另外一张表查询语句的条件)
1. 查询书籍id是15的出版社的名称 book_obj = models.Book.objects.filter(pk=15).first() # 先拿出id为15的书籍 print(book_obj.publish.name) # 外键关系在book内,正向查询,直接 . publish就行 2. 查询书籍id为15 的作者的姓名 book_obj = models.Book.objects.filter(pk=15).first() print(book_obj.authors.all()) res = book_obj.authors.all() for i in res: print(i.name) # 当我们在遇到app01.Author.None时,不要慌张,有可能是在authors后面缺少一个all() 3.查询作者是panshao的家庭住址 author_obj = models.Author.objects.filter(name='panshao').first() # 拿出叫panshao的这个人名字 print(author_obj.authordetail.addr) # 进行正向查询 4.查询出版社是攀少网络传媒出版社出版的书籍 publish_obj = models.Publish.objects.filter(name='攀少网络传媒出版').first() print(publish_obj.book_set.all()) 5. 查询作者是panshao的写过的所有的书籍 publish_obj = models.Author.objects.filter(name='panshao').first() print(publish_obj.book_set.all()) 6. 查询电话是130的作者名字 author_detail_obj = models.AuthorDetail,obj.filter(phone=130).first() print(author_detail_obj.author.name) # 当我们反向查询的结果是多个的时候就需要我们去加_set,,,,,,,,当你反向查询的结果是多个的时候 就需要加_set,,否则直接表明小写即可
查询书籍id为15的作者的电话号码 book_obj = models.Book.object.filter(pk=15).first() author_list = book_obj.authors.all() for author_obj in author_list: print(author_obj.author_detail.phone)
基于双下划线的跨表查询(连表查询)
1. 查询panshao作者的手机号 res = models.Author.objects.filter(name='panhao').values('author_detail__phone') # values是查询某张表的某个字段 print(res) 2. 查询手机号为130的作者姓名 author_detail_obj = models.AuthorDetail.objects.filter(phone=130).values('author__name') 3. 查询panshao这个作者的年龄和手机号: # 正向 res = models.Author.objects.filter(name='jason').values('age','author_detail__phone') print(res) 反向 res1 = models.AuthorDetail.objects.filter(author__name='jason').values('phone','author__age') print(res1) 4. 查询书籍id是1的作者的电话号码 res = models.Book.objects.filter(id=1).values('authors__authordetail__phone') res1 = models.Book.objects.filter(pk=1).values('外键字段1__外键字段2__外键字段3__普通字段') """只要表里面有外键字段 你就可以无限制跨多张表""
四. 聚合查询
1.统计所有书的总价格(aggregate) from django.db.models import Max,Min,Count,Avg,Sum res = models.Book.objects.aggregate(Sum('price')) res1 = models.Book.objects.aggregate(Avg('price')) res2 = models.Book.objects.aggregate(Count('price')) res3 = models.Book.objects.aggregate(Max('price')) res4 = models.Book.objects.aggregate(Min('price')) res5 = models.Book.objects.aggregate(Max('price'),Min('price'),Count('pk'),Avg('price'),Sum('price')) print(res5) print(res) print(res1) print(res2) 分组查询(关键字annotate) 统计每一本书的作者个数 from django.db.models import Max, Min, Count, Avg, Sum res = models.Book.objects.annotate(author_num = Count('authors')).values('author_num','title') # 按book分组,models后面跟什么就按什么分组 print(res) 统计出每个出版社卖的最便宜的书的价格 res = models.Publish.objects.annotate(mmp = Min('book__price')).values('name','mmp') print(res) 统计不止一个作者的图书 res = models.Book.objects.annotate(author_num=Count('authors')).filter(author_num__gt=1) print(res) """ 只要是queryset对象 就可以无限制的调用queryset对象的方法!!! 最最常用的就是对一个已经filter过滤完的数据 再进行更细化的筛选 """ # 查询各个作者出的书的总价格 # res = models.Author.objects.annotate(sp=Sum('book__price')).values('name','sp') # print(res)
五. F查询:
#F查询的本质就是从数据库中获取某个字段的值 # 查询库存数大于卖出数的书籍 """之前查询等号后面的条件都是我们认为输入的 现在变成了需要从数据库中获取数据放在等号后面 """ from django.db.models import F # res = models.Book.objects.filter(kucun__gt=F('maichu')) # print(res) # 将书籍库存数全部增加1000 # models.Book.objects.update(kucun=F('kucun')+1000) # 把所有书名后面加上'新款' # from django.db.models.functions import Concat # from django.db.models import Value # # ret3 = models.Book.objects.update(title=Concat(F('title'), Value('新款'))) # models.Book.objects.update(title = F('title')+'新款') # 不能这么写
六. q查询
# Q查询 from django.db.models import Q # 查询书籍名称是三国演义或者价格是444.44 # res = models.Book.objects.filter(title='三国演义',price=444.44) # filter只支持and关系 # res1 = models.Book.objects.filter(Q(title='三国演义'),Q(price=444)) # 如果用逗号 那么还是and关系 # res2 = models.Book.objects.filter(Q(title='三国演义')|Q(price=444)) # 或关系 # res3 = models.Book.objects.filter(~Q(title='三国演义')|Q(price=444)) # 不是三国演义的书籍 # print(res2) # Q高级用法 q = Q() q.connector = 'or' # 修改查询条件的关系 默认是and q.children.append(('title__contains','三国演义')) # 往列表中添加筛选条件 q.children.append(('price__gt',444)) # 往列表中添加筛选条件 res = models.Book.objects.filter(q) # filter支持你直接传q对象 但是默认还是and关系 print(res)