Django模型层(多表操作,一对一,一对多,多对多,以及 聚合/分组/F/Q查询)
创建表模型
(一)多表查询 一对一:models.OneToOneField(to_field='id',to='Authordatil') 一对多:(外键设置唯一性) models.ForeignKey(to='Publish',to_field='id') 多对多:自动生成第三张表 models.ManyToManyField(to='Author') (二)创建表模型 作者与详情 ( 一对一 ) 书与作者( 多对多 ) 出版社与书(一对多 ) 出版社:id name add email 书: id name price publish 作者: id name sex authordatil 详情: id photo address
from django.db import models # Create your models here. class Publish(models.Model):
# 主键 id = models.AutoField(primary_key=True) name = models.CharField(max_length=32) address = models.CharField(max_length=64) email = models.EmailField() def __str__(self): return '%s,%s,%s'%(self.name,self.address,self.email) class Book(models.Model): id = models.AutoField(primary_key=True) name = models.CharField(max_length=32) price = models.DecimalField(max_digits=5,decimal_places=2) # 出版社与书( 一对多 )外键要设置唯一性,关联的表,关联的字段 publish = models.ForeignKey(to='Publish',to_field='id') # 书与作者( 多对多 ) authors = models.ManyToManyField(to='Author') def __str__(self): return self.name class Author(models.Model): id = models.AutoField(primary_key=True) name = models.CharField(max_length=32)
choices = ( (0,"女"),(1,"男") )
sex = SmallIntegerField( choices=choices, default=0 )
# 作者与详情 ( 一对一 )
authordatil = models.OneToOneField(to_field='id',to='Authordatil')
def __str__(self):
return self.name
class Authordatil(models.Model):
id = models.AutoField(primary_key=True)
photo = models.CharField(max_length=32)
address = models.CharField(max_length=32)
id
字段是自动添加的- 对于外键字段,Django 会在字段名上添加"_id" 来创建数据库中的列名
- 外键字段 ForeignKey 有一个 null=True 的设置(它允许外键接受空值 NULL),你可以赋给它空值 None 。
添加/删除/修改表记录
批量插入
# 批量插入 bulk_create li = [] for i in range(100): li.append(models.Book(name='图书%s'%i,price=10+i)) # 生成100个对象 models.Book.objects.bulk_create(li) # 参数为列表,列表里为100个对象,一次插入100条
一对一 增/删/改 数据
Authordatil = Authordatil.objects.create(photo=121212121,address='东莞') # 增
auther = Auther.objects.filter(name = '西游记').update( publish_id=1 ) # 改
auther = Auther.objects.filter("name" = "西游记").first() # 先查
auther.name = "东游记" # 再改
auther.save() # 最后保存
auther =Auther.objects.filter(name = '西游记').delete() # 删除
一对多 增/删/改 数据
增: # publish_id: 数字 book = Book.objects.create(name='三国',price=56,publish_id=1) # publish: 对象 publish = Publish.objects.get(id=1) book = Book.objects.create(name='西游记',price=26,publish=publish)
改:
# publish_id: 数字
book = Book.objects.filter(name = '西游记').update( publish_id=1 )
# publish: 对象
publish = Publish.objects.get(id=3)
book = Book.objects.filter(name = '西游记').update(publish=publish)
删:同单表查询一样
book = Book.objects.filter(name='西游记',price=22).delete()
多对多 增/删/改 数据
增:add ( obj1,obj2... ) 可以传对象,可以传id,可以传多个
删:remove( obj1,obj2... ) 可以传对象,可以传id,可以传多个
改:先清空,配置
清空: clear() 清空所有
配置:set() 里面传列表,里面可以是对象或者是id *注意先清空,在配置
(五)多对多新增 (书 与 作者) 增:add ( obj1,obj2... ) 可以传对象,可以传id,可以传多个 zxx = Author.objects.filter(name='zxx').first() yxx = Author.objects.filter(name='yxx').first() book = Book.objects.filter(name='西游记').first() book.authors.add(yxx,zxx) 删:remove( obj1,obj2... ) 可以传对象,可以传id,可以传多个 lxx = Author.objects.filter(name='lxx').first() zxx = Author.objects.filter(name='zxx').first() book = Book.objects.filter(name='红楼梦').first() book.authors.remove(lxx,zxx) 清空: clear() 清空所有 book.authors.clear() 配置:set() 里面传列表,里面可以是对象或者是id *注意先清空,在配置 lxx = Author.objects.filter(name='lxx').first() zxx = Author.objects.filter(name='zxx').first() book.authors.set([1,2]) book.authors.set([lxx,zxx])
基于对象的查询表记录(子查询)
查询分析:正反向查询
从关联字段的表出发,查向另外一张表 为正向,反之为反向
eg: 正向: author---关联字段在author--->authordetail ------> 按字段 反向: authordetail------关联字段在author--->author -----> 按表名小写
一对一 查询
正向(按字段名) author = Author.objects.filter(name='lxx').first() photo = author.authordatil.photo 反向(按表名小写) authordatil = Authordatil.objects.filter(photo=166667878).first() author_name = authordatil.author.name
一对多 查询
正向(按字段名) book = Book.objects.filter(name='茶社').first() publish = book.publish print(publish.name) 反向 ( 按 表名_set.all() ) publish = Publish.objects.filter(name='武汉出版社').first() author = publish.book_set.all()
多对多 查询
多对多 正向(按字段名) book = Book.objects.filter(name='红楼梦').first() author = book.authors.all() 反向( 按 表名_set.all() ) author = Author.objects.filter( name='lxx').first() book = author.book_set.all()
基于双下划线查询表记录(连表查询)
*注意多用与查询某张表的字段名
正向查询 ( 按字段,跨表可以在filter,也可以在values中 ) photo = Author.objects.filter(name='lxx').values('authordatil__photo') print(photo) 反向查询( 按表名小写,跨表可以在filter,也可以在values中 ) photo = Authordatil.objects.filter(author__name='lxx').values('photo') print(photo) 正向连续跨表查询( 按字段,跨表可以在filter,也可以在values中 )
name = Publish.objects.filter(name='武汉出版社').values('book__authors__authordatil__photo')
反向连续跨表查询( 按表名小写,跨表可以在filter,也可以在values中 )
photo = Author.objects.filter(book__publish__name='武汉出版社').values('book__authors__authordatil__photo')
练习:
# 练习: 查询苹果出版社出版过的所有书籍的名字与价格(一对多)
# 练习: 查询alex出过的所有书籍的名字(多对多)
# 练习: 查询人民出版社出版过的所有书籍的名字以及作者的姓名
# 练习: 手机号以151开头的作者出版过的所有书籍名称以及出版社名称
# 练习: 查询人民出版社出版过的所有书籍的名字与价格(一对多)
练习一:
1 用orm插入6个作者详情,插入6个作者
2 用orm插入6个出版社
3 用orm插入8本书,4种用publish=对象的形式,4种用publish_id=id的形式
4 两种形式修改书的出版社
5 基于对象查询,地址为山东的作者的姓名 ( 一对一 )
6 基于对象查询,电话为13888888作者的性别 ( 一对一 )
7 基于双下划线查询,地址为山东的作者的姓名
8 基于双下划线查询,电话为13888888作者的性别
9 基于对象查询北京出版社出版的所有图书
9 基于对象查询南京出版社出版的价格大于20的图书(一对多)
10 基于对象查询图书名字叫红楼梦这本书的作者(多对多)
11基于对象查询作者名为lqz,出版的所有图书
12 基于对象查询作者名为lqz,出版的价格大于20的所有图书
13 基于对象查询图书名以红开头,所有的出版社信息
14基于对象查询图书名以红开头,出版社以社结尾的所有出版社地址
15 查询所有价格大于20的图书
16 查询所有价格大于20的图书所有的出版社
17 查询手机号以13开头,所有的作者
18查询手机号以13开头,所有作者出版的所有图书
19 查询图书价格在[19.9,56,77.9]内的所有图书
20 查询图书价格在[19.9,56,77.9]内的所有图书,对应的所有作者
21 查询图书价格在[19.9,56,77.9]内的所有图书,对应的所有作者的手机号
4 两种形式修改书的出版社 方法(1) book = Book.objects.filter(name='红楼梦').first() book.publish_id = 2 book.publish = publish book.save() 方法(2) publish = Publish.objects.filter(name='深圳出版社').first() book = Book.objects.filter(name='红楼梦').first() book.publish = publish book.save() 5 基于对象查询,地址为山东的作者的姓名 ( 一对一 ) authordatil = Authordatil.objects.filter(address='山东').first() name = authordatil.author.name 6 基于对象查询,电话为13888888作者的性别 ( 一对一 ) authordatil = Authordatil.objects.filter(photo = 133323223).first() sex = authordatil.author.sex 7 基于双下划线查询,地址为山东的作者的姓名 authordatil = Authordatil.objects.filter(address='东京').values('author__name') 8 基于双下划线查询,电话为13888888作者的性别 sex = Authordatil.objects.first(photo=133323223).values('author__sex') 9 基于对象查询北京出版社出版的所有图书 publish = Publish.objects.filter(name='杭州出版社').first() books = publish.book_set.all() 9 基于对象查询南京出版社出版的价格大于20的图书(一对多) publish = Publish.objects.filter(name='杭州出版社').first() book = publish.book_set.all().filter(price__gt=20) 10 基于对象查询图书名字叫红楼梦这本书的作者(多对多) book = Book.objects.filter(name='红楼梦').first() authors = book.authors.all().values('name') 11基于对象查询作者名为lqz,出版的所有图书 author = Author.objects.filter(name='lxx').first() book = author.book_set.all() 12 基于对象查询作者名为lqz,出版的价格大于20的所有图书 author = Author.objects.filter(name='lxx').first() book = author.book_set.all().filter(price__gt=31) 13 基于对象查询图书名以红开头,所有的出版社信息 book = Book.objects.filter(name__startswith='红').first() publish = book.publish 14基于对象查询图书名以红开头,出版社以社结尾的所有出版社地址 book = Book.objects.filter(name__startswith='红').values_list('publish__address') print(book) publish = Publish.objects.filter(address__endswith='社').values_list('address') for i in publish: if i in book: print(i) 15 查询所有价格大于20的图书 book = Book.objects.filter(price__gt=20).all() 16 查询所有价格大于20的图书所有的出版社 books = Book.objects.filter(price__gt=20) for book in books: publish = book.publish print(publish) 17 查询手机号以13开头,所有的作者 方法一: authordatils = Authordatil.objects.filter(photo__startswith=13).values('author__name') 方法二: authordatils = Authordatil.objects.filter(photo__startswith=13) for authordatil in authordatils: author = authordatil.author print(author) 18查询手机号以13开头,所有作者出版的所有图书 方法一: books = Authordatil.objects.filter(photo__startswith=13).values('author__book__name') 方法二: authordatils = Authordatil.objects.filter(photo__startswith=13).first() # 拿到信息表 再到作者 再到书 book = authordatils.author.book_set.all() 19 查询图书价格在[19.9,56,77.9]内的所有图书 book = Book.objects.filter(price__in=[19.9, 56, 77.9]).all() 20 查询图书价格在[19.9,56,77.9]内的所有图书,对应的所有作者 方法一: author = Book.objects.filter(price__in=[19.9, 56, 77.9]).values('authors') 方法二: books = Book.objects.filter(price__in=[19.9, 56, 77.9]) for book in books: author = book.authors.all() 21 查询图书价格在[19.9,56,77.9]内的所有图书,对应的所有作者的手机号 方法一: books = Book.objects.filter(price__in=[19.9, 56, 77.9]).values('authors__authordatil__photo') 方法二: books = Book.objects.filter(price__in=[19.9, 56, 77.9]).all() for book in books: authors = book.authors.all() for author in authors: photo = author.authordatil.photo print(photo)
作业二:
1 修改名字为lqz作者的手机号为12345678901
2 修改以红开头的图书的所有作者手机号为123321321
3 修改出版社是北京出版社出版的所有图书的价格为99.99
4 为红楼梦这本书,新增lqz,egon两个作者(两种方式)
5 更改红楼梦这本书的作者为lqz,xiaohou(两种方式) ??????????
6 把上题lxx 换成 egon
6 删除红楼梦这本书xiaohou这个作者
7 清空红楼梦这本书所有作者
8 查询图书价格大于30的所有作者的名字
9 查询北京出版社出版的所有图书的作者名字
10 查询北京出版社出版的所有图书的作者手机号
11 查询西游记这本书所有作者的个数
12 查询北京出版社出版的图书个数
1 修改名字为lqz作者的手机号为12345678901 author = Author.objects.filter(name='lxx').first() authordatil = author.authordatil authordatil.photo = 12345678901 authordatil.save() 2 修改以红开头的图书的所有作者手机号为123321321 books = Book.objects.filter(name__startswith='红') for book in books: authors = book.authors.all() for author in authors: authordatil = author.authordatil authordatil.photo = 123321321 authordatil.save() 3 修改出版社是北京出版社出版的所有图书的价格为99.99 publish = Publish.objects.filter(name='长沙出版社').first() books = publish.book_set.all() for book in books: book.price = 99.99 book.save() print(book.price) 4 为红楼梦这本书,新增lqz,egon两个作者(两种方式) 方法一: book = Book.objects.filter(name='红楼梦').first() book.authors.add(4,5) 方法二: egon = Author.objects.filter(name= 'egon').first() wxx = Author.objects.filter(name='wxx').first() book = Book.objects.filter(name='红楼梦').first() book.authors.add(egon,wxx) 5 更改红楼梦这本书的作者为lqz,xiaohou(两种方式) ?????????? book = Book.objects.filter(name='红楼梦').first() authors = book.authors.clear() print(authors) lxx = Author.objects.filter(name='lxx').first() zxx = Author.objects.filter(name='zxx').first() book.authors.set([lxx,zxx]) 把lxx 换成 egon lxx =Author.objects.filter(name='lxx').first() egon = Author.objects.filter(name='egon').first() book = Book.objects.filter(name='边城').first() book.authors.remove(lxx) book.authors.set([egon,]) 6 删除红楼梦这本书xiaohou这个作者 lxx = Author.objects.filter(name='lxx').first() book = Book.objects.filter(name='红楼梦').first() book.authors.remove(lxx) 7 清空红楼梦这本书所有作者 book = Book.objects.filter(name='红楼梦').first() book.authors.clear() 8 查询图书价格大于30的所有作者的名字 book = Book.objects.filter(price__gt=30).values('authors__name','price') 9 查询北京出版社出版的所有图书的作者名字 publish = Publish.objects.filter(name='武汉出版社').values('book__authors__name') 10 查询北京出版社出版的所有图书的作者手机号 name = Publish.objects.filter(name='武汉出版社').values('book__authors__authordatil__photo') 11 查询西游记这本书所有作者的个数 book = Book.objects.filter(name='西游记').first() con = book.authors.all().count() 12 查询北京出版社出版的图书个数 publish = Publish.objects.filter(name='武汉出版社').first() con = publish.book_set.all().count()
聚合查询(aggregate)
from django.db.models import Avg,Count,Min,Max,Sum con = Book.objects.all().aggregate(Max('price'),Min('price'),Avg('price'),Sum('price'))
# 计算所有图书的平均价格
# 计算图书的最高价格
# 计算图书的最高价格,最低价格,平均价格,总价
计算所有图书的平均价格 con = Book.objects.all().aggregate(Avg('price')) print(con) 计算图书的最高价格 con = Book.objects.all().aggregate(Max('price')) print(con) 计算图书的最高价格,最低价格,平均价格,总价 con = Book.objects.all().aggregate(Max('price'),Min('price'),Avg('price'),Sum('price')) print(con)
分组查询(annotate)
group by 谁,就以谁做基表,filter过滤,annotate取分组,values取值
values在前 以谁分组 values在后 表示 取值
filter在前 where条件 filter在后 表示 having
# 统计每一本书作者个数
# 统计每一个出版社的最便宜的书(以谁group by 就以谁为基表)
# 统计每一本以py开头的书籍的作者个数
# 统计每一本以py开头的书籍的作者个数--套用模板
# 查询各个作者出的书的总价格
# 查询名字叫lqz作者书的总价格
# 查询所有作者写的书的总价格大于30
# 统计不止一个作者的图书
# 统计每一本书作者个数 con = Book.objects.values('pk').all().annotate(c= Count('authors')).values('name','c') print(con) # 统计每一个出版社的最便宜的书 con = Publish.objects.values('pk').all().annotate(c= Min('book__price')).values('name','c') print(con) # 统计每一本以py开头的书籍的作者个数 con = Book.objects.filter(name__startswith='红').annotate(c=Count('authors')).values('name','c') print(con) # 统计每一本以py开头的书籍的作者个数--套用模板 con = Book.objects.filter(name__startswith='红').all().annotate(c=Count('authors')).values('name', 'c') print(con) # 查询各个作者出的书的总价格 con = Author.objects.annotate(c= Sum('book__price')).values('name','c') print(con) # 查询名字叫lqz作者书的总价格 con = Author.objects.filter(name='egon').annotate(c= Sum('book__price')).values('name','c') print(con) # 查询所有作者写的书的总价格大于30 con = Author.objects.annotate(c=Sum('book__price')).filter(c__gt=70).values('name', 'c') print(con) # 统计不止一个作者的图书 con = Book.objects.annotate(c = Count('authors')).filter(c__gt=1).values('name','c') print(con) # F查询 # 查询评论数大于阅读数的书 from django.db.models import F con = Book.objects.filter(commit_num__gt=F('read_num')).values('name') print(con) # 把所有书的评论数加1 con = Book.objects.all().update(commit_num=F('read_num')+1) print(con) # 把python这本书的阅读数减5 con = Book.objects.filter(name='红楼梦').all().update(read_num=F('read_num') -5 ) print(con)
F查询
无法引用字段上的值,把字段名的值包裹起来,就能用了
from django.db.models import F
eg: Book.objects.filter(id=1).update(F("price")+1) # 将第一本书的价格加一
# 查询评论数大于阅读数的书
# 把所有书的评论数加1
# 把python这本书的阅读数减5
# 查询评论数大于阅读数的书 from django.db.models import F con = Book.objects.filter(commit_num__gt=F('read_num')).values('name') print(con) # 把所有书的评论数加1 con = Book.objects.all().update(commit_num=F('read_num')+1) print(con) # 把python这本书的阅读数减5 con = Book.objects.filter(name='红楼梦').all().update(read_num=F('read_num') -5 ) print(con)
Q查询
from django.db.models import Q
与 & 或 | 非 ~
Q查询: 基于关系运算 &与 |或 ~非
&与 (默认为 与)
Book.objects.filter(Q(price=15),Q(name="金瓶眉”)) # 查看价格为15 与(且) 名字为金瓶眉的书
|或
Book.objects.filter(Q(price=15) | Q(name="金瓶眉”)) # 查看价格为15 或 名字为金瓶眉的书
~非
Book.objects.filter(~Q(price=15) & ~Q(name="金瓶眉”)) # 查看价格不为15 且 名不为金瓶眉的书
# 查询作者名字是lqz或者名字是egon的书
# 查询作者不是lqz的书
构建很复杂的逻辑,需要用括号来区分
from django.db.models import Q # 查询作者名字是lqz或者名字是egon的书 con = Author.objects.filter(Q(name='lxx') | Q(name='wxx')).values('book__name') print(con) # 查询作者不是lqz的书 # con = Author.objects.filter(~Q(name='lxx')).values('book__name') con = Author.objects.filter(~Q(name='lxx')).values('book__name') print(con) con = Book.objects.filter(~Q(authors__name='lxx')).values('name') print(con) # 构建很复杂的逻辑, 需要用括号来区分 ret = Book.objects.filter((Q(name='红楼梦') & Q(price__gt=100)) | Q(pk__gt=2)) print(ret)