010---Django的模型层(2)
确定模型关系:
''' Publish ---- Book 多对一:一个出版社对应多本书,在多的那张表添加关联字段 Book ---- Author 多对多:一个书对应多个作者,多个作者对应一本书 会自动创建第三张表 Author ---- AuthorDetail 一对一:一个作者对应一个作者信息 注意: 主键可不加,django会默认添加字段为id的主键 Django2版本会强制要求在Foreignkey添加这条参数,django1版本默认添加 on_delete = models.CASCADE '''
模型建立如下:
1 from django.db import models 2 3 # 作者详情表 4 class AuthorDetail(models.Model): 5 nid = models.AutoField(primary_key=True) 6 birthday = models.DateField(verbose_name='生日') 7 telephone = models.BigIntegerField(verbose_name='手机号') 8 address = models.CharField(max_length=64) 9 10 11 # 出版社表 12 class Publish(models.Model): 13 nid = models.AutoField(primary_key=True) 14 name = models.CharField(max_length=32, verbose_name="出版社名称") 15 city = models.CharField(max_length=32, verbose_name='城市') 16 email = models.EmailField(verbose_name='邮箱') 17 18 def __str__(self): 19 return self.name 20 21 # 书籍表 22 class Book(models.Model): 23 nid = models.AutoField(primary_key=True) 24 title = models.CharField(max_length=32, verbose_name="书籍名称") 25 publish_date = models.DateField(verbose_name='出版日期') 26 price = models.DecimalField(max_digits=5, decimal_places=2, verbose_name='价格') 27 read_num = models.IntegerField(verbose_name='阅读数',default=0) 28 comment_num = models.IntegerField(verbose_name='评论数',default=0) 29 30 publish = models.ForeignKey(to='Publish', to_field='nid', verbose_name='出版社',on_delete=models.CASCADE) 31 author = models.ManyToManyField(to='Author',verbose_name='作者') 32 33 def __str__(self): 34 return self.title 35 36 # 作者表 37 class Author(models.Model): 38 nid = models.AutoField(primary_key=True) 39 name = models.CharField(max_length=32, verbose_name="作者名称") 40 age = models.IntegerField(verbose_name='作者年龄') 41 author_detail = models.OneToOneField(to='AuthorDetail', to_field='nid', unique=True,on_delete=models.CASCADE) 42 43 def __str__(self): 44 return self.name
会生成五张表:
注意事项:
- 表的名称:
myapp_modelName
,是根据 模型中的元数据自动生成的,也可以覆写为别的名称 - id字段是自动添加的。主键也可以不自己写
- 对于外键字段。django会在字段名上添加‘_id’来创建数据库的列名
- 定义好模型之后,要告诉django使用这些模型,就要在settings.py文件中的INSTALL_APPS中设置,添加models.py文件所在应用的app名称
- 外键字段 ForeignKey 有一个 null=True 的设置(它允许外键接受空值 NULL),你可以赋给它空值 None
添加表记录:
首先添加一个出版社:
1 pub = Publish.objects.create(name='人民出版社',email='123@qq.com',city='北京')
然后就可以为书籍绑定关系:
1 ===========================绑定一对多关系=========================== 2 为book表绑定关系: publish --- book 3 方式1 4 book_obj = Book.objects.create(title='红楼梦',price=100,publish_date='2018-08-08',publish_id=1) 5 print(book_obj.title) 6 7 方式2 8 pub_obj = Publish.objects.filter(nid=1).first() 9 book_obj = Book.objects.create(title='西游记',price=100,publish_date='2018-07-07',publish=pub_obj) 10 print(book_obj.title) 11 print(book_obj.publish_id) 12 print(book_obj.publish) # 与这本书籍关联的出版社对象 13 print(book_obj.publish.name)
1 ===========================绑定多对多关系=========================== 2 book_obj = Book.objects.create(title='金瓶子', price=100, publish_date='2018-07-07', publish_id=1) 3 alex = Author.objects.get(name='alex') 4 egon = Author.objects.get(name='egon') 5 # 绑定多对多关系的API 6 7 book_obj.author.add(egon,alex) 8 book_obj.author.add(1,2) 9 book_obj.author.add(*[1,2,3]) 10 11 解除多对多关系 12 book = Book.objects.filter(title='金瓶子').first() 13 book.author.remove(2) 14 # 清除所有 15 book.author.clear() 16 # 获取所有 17 # 查询主键为4的数据所有作者的名字 18 print(book.author.all()) # <QuerySet [<Author: alex>]> 19 print(book.author.all().values('name','age')) # <QuerySet [{'name': 'alex', 'age': 33}]>
跨表查询:
- 基于对象查询
- 基于双下划线查询
- 聚合和分组查询
- F 和 Q查询
1 ------------------------------------1、基于对象查询(子查询)------------------------------------ 2 3 一对多的正向查询:查询金瓶子这本书的出版社的名字 4 book_obj = Book.objects.filter(title='金瓶子').first() 5 print(book_obj.publish) # 与这本书关联的出版社对象 6 print(book_obj.publish.name) 7 8 一对多的反向查询:查询人民出版社出版的书 9 publish = Publish.objects.filter(name='人民出版社').first() 10 ret = publish.book_set.all() 11 print(ret) 12 13 多对多的正向查询:查询金瓶子这本书的所有作者名字 14 book.author.all() 15 16 多对多的反向查询:查询alex写的所有的书 17 author = Author.objects.filter(name='alex').first() 18 ret = author.book_set.all() 19 20 一对一的正向查询:查询egon的作者信息 21 author = Author.objects.filter(name='egon').first() 22 ret = author.author_detail.telephone 23 24 一对一的反向查询:查询手机号为110的作者的名字和年龄 25 detail = AuthorDetail.objects.filter(telephone='110').first() 26 ret = detail.author 27 print(ret.name,ret.age) # alex 33
'''
A--B
关联属性在A表中
正向查询: A--B
反向查询: B--A
# 一对多查询
正向查询:按字段
反向查询:按表名(小写)_set.all()
# 多对多查询
正向查询:按字段
反向查询:按表名(小写)_set.all()
# 一对一查询
正向查询:按字段
反向查询:按表名(小写) 因为一对一查询的只有单条,所以没有all set
'''
1 # ------------------------------------2、基于双下划线的跨表查询(join查询)------------------------------------ 2 # 一对多:查询金瓶子这本书的出版社的名字 3 4 # 方式1:正向查询: 5 # ret= Book.objects.filter(title='金瓶子').values('publish__name') 6 # print(ret) # <QuerySet [{'publish__name': '人民出版社'}]> 7 8 # 方式2:反向查询: 9 # Publish.objects.filter(book__title='金瓶子').values('name') 10 11 # 多对多:查询西游记这本书的所有作者名字 12 13 # 正向:按字段 14 # ret = Book.objects.filter(title='西游记').values('author__name') 15 # print(ret) 16 17 # 反向:按表名 18 # ret = Author.objects.filter(book__title='西游记').values('name') 19 # print(ret) 20 21 # 一对一查询 :查询alex的手机号 22 23 # 正向 24 # ret = Author.objects.filter(name='alex').values('author_detail__telephone') 25 # print(ret) # <QuerySet [{'author_detail__telephone': 110}]> 26 27 # 反向 28 # ret1 = AuthorDetail.objects.filter(author__name='alex').values('telephone') 29 # print(ret1) # <QuerySet [{'telephone': 110}]> 30 31 # 查询手机号以110开头的作者出版过的所有书籍名称以及书籍出版社名称 32 33 # 正向 34 # ret3 = Book.objects.filter(author__author_detail__telephone__startswith='110').values('title', 'publish__name') 35 # print(ret3) # <QuerySet [{'title': '西游记', 'publish__name': '人民出版社'}, {'title': '金瓶子', 'publish__name': '人民出版社'}]> 36 37 # 反向 38 # ret4 = Author.objects.filter(author_detail__telephone__startswith='110').values('book__title', 39 # 'book__publish__name') 40 # print(ret4) # <QuerySet [{'book__title': '金瓶子', 'book__publish__name': '人民出版社'}, {'book__title': '西游记', 'book__publish__name': '人民出版社'}]>
1 # ------------------------------------3、聚合查询aggregate:返回的是一个字典,不再是queryset集合------------------------------------ 2 from django.db.models import Avg, Max, Min, Count 3 # 查询所有书籍的平均价格 4 # ret = Book.objects.all().aggregate(Avg('price')) 5 # ret1 = Book.objects.all().aggregate(price=Avg('price'),max_price=Max('price')) 6 # print(ret) # {'price__avg': 97.0} 7 # print(ret1) # {'price': 97.0, 'max_price': 100.0}
1 # ------------------------------------3、分组查询 annotate 返回值依然是queryset------------------------------------ 2 # 单表分组查询:在单表下,按照主键分组没有任何意义 3 4 # 查询每一个部门的名称及员工的平均薪水 5 # select dep,Avg(salary) from emp group by dep 6 # ret = Emp.objects.values('dep').annotate(avg_salary=Avg('salary')) 7 # print(ret) # <QuerySet [{'dep': '销售部', 'avg_salary': 2000.0}, {'dep': '技术部', 'avg_salary': 3500.0}, {'dep': '人事部', 'avg_salary': 9000.0}]> 8 9 # 查询每一个省份的名称及员工数 10 11 # ret1 = Emp.objects.values('province').annotate(emp_count=Count('id')) 12 # print(ret1) # <QuerySet [{'province': '江西', 'emp_count': 2}, {'province': '北京', 'emp_count': 1}, {'province': '南昌', 'emp_count': 1}]> 13 14 # 查询每一个出版社的名字及出版的书的个数 15 # ret = Publish.objects.values('name').annotate(Count('book')) 16 # print(ret) # <QuerySet [{'name': '人民出版社', 'book__count': 3}, {'name': '南京出版社', 'book__count': 1}]> 17 18 # ret1 = Book.objects.values('publish__name').annotate(Count('nid')) 19 # print(ret1) # <QuerySet [{'publish__name': '人民出版社', 'nid__count': 3}, {'publish__name': '南京出版社', 'nid__count': 1}]> 20 21 # ret2 = Publish.objects.values('nid').annotate(c=Count('book__title')).values('name','c') 22 # print(ret2) 23 24 25 # 查询每个作者的名字及出版过的书籍的最高价格 26 # ret3 = Author.objects.values('nid').annotate(m=Max('book__price')).values('name','m') 27 # print(ret3) 28 29 # 总结跨表的分组查询的模型 30 # 每一个表的模型.objects.values('pk').annotate(聚合函数(关联表__字段)).values所有字段) 31 # 每一个表的模型.objects.annotate(聚合函数(关联表__字段)).values(所有字段) 32 33 # 查询每一个书籍的名称对应的作者个数 34 # ret4 = Book.objects.values('pk').annotate(c=Count('author__name')).values('title','c') 35 # print(ret4) 36 37 38 # 统计每一本以py开头的书籍的作者个数: 39 # ret5 = Book.objects.filter(title__startswith='py').annotate(c=Count('author__name')).values('title','c') 40 # print(ret5) 41 42 # 统计不止一个作者的书籍 43 # ret6 = Book.objects.values('pk').annotate(c=Count('author__name')).filter(c__gt=1).values('title','c') 44 # print(ret6)
总结跨表的分组查询模型:
1 每一个表的模型.objects.values('pk').annotate(聚合函数('关联表__字段').values('字段1',‘字段2’)) 2 3 每一个表的模型.objects.annotate(聚合函数(关联表__字段)).values(所有字段)
练习:
1 # 查询每一个书籍的名称对应的作者个数 2 3 # ret4 = Book.objects.values('pk').annotate(c=Count('author__name')).values('title','c') 4 # print(ret4) 5 6 7 # 统计每一本以py开头的书籍的作者个数: 8 # ret5 = Book.objects.filter(title__startswith='py').annotate(c=Count('author__name')).values('title','c') 9 # print(ret5) 10 11 # 统计不止一个作者的书籍 12 # ret6 = Book.objects.values('pk').annotate(c=Count('author__name')).filter(c__gt=1).values('title','c') 13 # print(ret6)
1 # ------------------------------------F和Q查询------------------------------------ 2 from django.db.models import F,Q 3 4 # 查询评论数大于阅读数的 5 # ret = Book.objects.filter(read_num__gt=F('comment_num')) 6 # print(ret) 7 8 # 所有书籍的价格+10 9 # ret = Book.objects.all().update(price=F('price')+1) 10 # print(ret) 11 12 # 复杂的条件查询 13 14 # 查询书籍名称是红楼梦 且 价格是78的书籍 15 ret = Book.objects.filter(Q(title='红楼梦')&Q(price=78)) 16 # 查询书籍名称是红楼梦 或 价格是78的书籍 17 Book.objects.filter(Q(title='红楼梦')|Q(price=78)) 18 # 取反 非的意思 名称不是红楼梦或者价格等于78 19 Book.objects.filter(~Q(title='红楼梦')|Q(price=78)) 20 print(ret)