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}]>

 

跨表查询:

  1. 基于对象查询
  2. 基于双下划线查询
  3. 聚合和分组查询
  4. 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)

 

posted @ 2018-11-06 11:26  爬呀爬Xjm  阅读(186)  评论(0编辑  收藏  举报