65-django-模型层、聚合函数、分组查询、F与Q查询、orm开启事务、orm常用字段及参数、数据库优化、图书管理系统讲解
今日内容概要
- 聚合查询(聚合函数的使用)aggregate
- max、min、sum、count、avg
- 分组查询(group by的使用)annotate
- F与Q查询
- django中如何开启事务
- orm中常用字段及参数
- 数据库查询优化(only与defer、select_related与prefetch_related)
- 图书管理系统作业讲解
今日内容详细
聚合查询
1 # 聚合查询 单独使用需要用 aggregate 2 """ 3 聚合查询通常情况下都是配合分组一起使用的 4 只要是跟数据库相关的模块 5 基本上都在django.db.models里面 6 如果上述没有那么应该在django.db里面 7 """ 8 from app01 import models 9 from django.db.models import Max,Min,Sum,Count,Avg 10 # 1 所有书的平均价格 11 # res = models.Book.objects.aggregate(Avg('price')) 12 # print(res) 13 # 2.上述方法一次性使用 14 res = models.Book.objects.aggregate(Max('price'),Min('price'),Sum('price'),Count('pk'),Avg('price')) 15 print(res)
分组查询
1 # 分组查询 annotate 2 """ 3 MySQL分组查询都有哪些特点 4 分组之后默认只能获取到分组的依据 组内其他字段都无法直接获取了 5 严格模式 6 ONLY_FULL_GROUP_BY 7 8 """ 9 from django.db.models import Max, Min, Sum, Count, Avg 10 # 1.统计每一本书的作者个数 11 # res = models.Book.objects.annotate() # models后面点什么 就是按什么分组 12 # res = models.Book.objects.annotate(author_num=Count('authors')).values('title','author_num') 13 """ 14 author_num是我们自己定义的字段 用来存储统计出来的每本书对应的作者个数 15 """ 16 # res1 = models.Book.objects.annotate(author_num=Count('authors__id')).values('title','author_num') 17 # print(res,res1) 18 """ 19 代码没有补全 不要怕 正常写 20 补全给你是pycharm给你的 到后面在服务器上直接书写代码 什么补全都没有 颜色提示也没有 21 22 """ 23 24 # 2.统计每个出版社卖的最便宜的书的价格(作业:复习原生SQL语句 写出来) 25 # res = models.Publish.objects.annotate(min_price=Min('book__price')).values('name','min_price') 26 # print(res) 27 28 # 3.统计不止一个作者的图书 29 # 1.先按照图书分组 求每一本书对应的作者个数 30 # 2.过滤出不止一个作者的图书 31 # res = models.Book.objects.annotate(author_num=Count('authors')).filter(author_num__gt=1).values('title','author_num') 32 # """ 33 # 只要你的orm语句得出的结果还是一个queryset对象 34 # 那么它就可以继续无限制的点queryset对象封装的方法 35 # 36 # """ 37 # print(res) 38 39 # 4.查询每个作者出的书的总价格 40 # res = models.Author.objects.annotate(sum_price=Sum('book__price')).values('name','sum_price') 41 # print(res) 42 43 """ 44 如果我想按照指定的字段分组该如何处理呢? 45 models.Book.objects.values('price').annotate() 46 后续BBS作业会使用 47 48 49 你们的机器上如果出现分组查询报错的情况 50 你需要修改数据库严格模式 51 """
F与Q查询
1 # F查询 2 # 1.查询卖出数大于库存数的书籍 3 # F查询 4 """ 5 能够帮助你直接获取到表中某个字段对应的数据当作查询条件 6 """ 7 from django.db.models import F 8 # res = models.Book.objects.filter(maichu__gt=F('kucun')) 9 # print(res) 10 11 12 # 2.将所有书籍的价格提升500块 13 # models.Book.objects.update(price=F('price') + 500) 14 15 16 # 3.将所有书的名称后面加上爆款两个字 17 """ 18 在操作字符类型的数据的时候 F不能够直接做到字符串的拼接 19 """ 20 from django.db.models.functions import Concat 21 from django.db.models import Value 22 models.Book.objects.update(title=Concat(F('title'), Value('爆款'))) 23 # models.Book.objects.update(title=F('title') + '爆款') # 所有的名称会全部变成空白 24 25 26 27 28 29 # Q查询 30 # 1.查询卖出数大于100或者价格小于600的书籍 31 # res = models.Book.objects.filter(maichu__gt=100,price__lt=600) 32 """filter括号内多个参数是and关系""" 33 from django.db.models import Q 34 # res = models.Book.objects.filter(Q(maichu__gt=100),Q(price__lt=600)) # Q包裹逗号分割 还是and关系 35 # res = models.Book.objects.filter(Q(maichu__gt=100)|Q(price__lt=600)) # | or关系 36 # res = models.Book.objects.filter(~Q(maichu__gt=100)|Q(price__lt=600)) # ~ not关系 37 # print(res) # <QuerySet []> 38 39 # Q的高阶用法 能够将查询条件的左边也变成字符串的形式 40 q = Q() 41 q.connector = 'or' 42 q.children.append(('maichu__gt',100)) 43 q.children.append(('price__lt',600)) 44 res = models.Book.objects.filter(q) # 默认还是and关系 45 print(res)
django中如何开启事务
1 """ 2 事务 3 ACID 4 原子性 5 不可分割的最小单位 6 一致性 7 跟原子性是相辅相成 8 隔离性 9 事务之间互相不干扰 10 持久性 11 事务一旦确认永久生效 12 13 事务的回滚 14 rollback 15 事务的确认 16 commit 17 """ 18 # 目前你只需要掌握Django中如何简单的开启事务 19 # 事务 20 from django.db import transaction 21 try: 22 with transaction.atomic(): 23 # sql1 24 # sql2 25 ... 26 # 在with代码快内书写的所有orm操作都是属于同一个事务 27 except Exception as e: 28 print(e) 29 print('执行其他操作')
orm中常用字段及参数
AutoField 主键字段 primary_key=True CharField 对应数据库的varchar verbose_name 字段的注释 max_length 长度 IntegerField int BigIntegerField bigint DecimalField max_digits=8 最大位数 decimal_places=2 小数点后位数 EmailFiled 对应数据库的 varchar(254) DateField 对应数据库 date DateTimeField 对应数据库 datetime auto_now:每次修改数据的时候都会自动更新当前时间 auto_now_add:只在创建数据的时候记录创建时间后续不会自动修改了 BooleanField(Field) - 布尔值类型 该字段传布尔值(False/True) 数据库里面存0/1 TextField(Field) - 文本类型 该字段可以用来存大段内容(文章、博客...) 没有字数限制 后面的bbs作业 文章字段用的就是TextField FileField(Field) - 字符类型 upload_to = "/data" 给该字段传一个文件对象,会自动将文件保存到/data目录下然后将文件路径保存到数据库中 /data/a.txt 后面bbs作业也会涉及 # 更多字段 直接参考博客:https://www.cnblogs.com/Dominic-Ji/p/9203990.html # django除了给你提供了很多字段类型之外 还支持你自定义字段 class MyCharField(models.Field): def __init__(self,max_length,*args,**kwargs): self.max_length = max_length # 调用父类的init方法 super().__init__(max_length=max_length,*args,**kwargs) # 一定要是关键字的形式传入 def db_type(self, connection): """ 返回真正的数据类型及各种约束条件 :param connection: :return: """ return 'char(%s)'%self.max_length # 自定义字段使用 myfield = MyCharField(max_length=16,null=True) # 外键字段及参数 unique=True ForeignKey(unique=True) === OneToOneField() # 你在用前面字段创建一对一 orm会有一个提示信息 orm推荐你使用后者但是前者也能用 db_index 如果db_index=True 则代表着为此字段设置索引 (复习索引是什么) to_field 设置要关联的表的字段 默认不写关联的就是另外一张的主键字段 on_delete 当删除关联表中的数据时,当前表与其关联的行的行为。 """ django2.X及以上版本 需要你自己指定外键字段的级联更新级联删除 """
字段合集
AutoField(Field) - int自增列,必须填入参数 primary_key=True BigAutoField(AutoField) - bigint自增列,必须填入参数 primary_key=True 注:当model中如果没有自增列,则自动会创建一个列名为id的列 from django.db import models class UserInfo(models.Model): # 自动创建一个列名为id的且为自增的整数列 username = models.CharField(max_length=32) class Group(models.Model): # 自定义自增列 nid = models.AutoField(primary_key=True) name = models.CharField(max_length=32) SmallIntegerField(IntegerField): - 小整数 -32768 ~ 32767 PositiveSmallIntegerField(PositiveIntegerRelDbTypeMixin, IntegerField) - 正小整数 0 ~ 32767 IntegerField(Field) - 整数列(有符号的) -2147483648 ~ 2147483647 PositiveIntegerField(PositiveIntegerRelDbTypeMixin, IntegerField) - 正整数 0 ~ 2147483647 BigIntegerField(IntegerField): - 长整型(有符号的) -9223372036854775808 ~ 9223372036854775807 BooleanField(Field) - 布尔值类型 NullBooleanField(Field): - 可以为空的布尔值 CharField(Field) - 字符类型 - 必须提供max_length参数, max_length表示字符长度 TextField(Field) - 文本类型 EmailField(CharField): - 字符串类型,Django Admin以及ModelForm中提供验证机制 IPAddressField(Field) - 字符串类型,Django Admin以及ModelForm中提供验证 IPV4 机制 GenericIPAddressField(Field) - 字符串类型,Django Admin以及ModelForm中提供验证 Ipv4和Ipv6 - 参数: protocol,用于指定Ipv4或Ipv6, 'both',"ipv4","ipv6" unpack_ipv4, 如果指定为True,则输入::ffff:192.0.2.1时候,可解析为192.0.2.1,开启此功能,需要protocol="both" URLField(CharField) - 字符串类型,Django Admin以及ModelForm中提供验证 URL SlugField(CharField) - 字符串类型,Django Admin以及ModelForm中提供验证支持 字母、数字、下划线、连接符(减号) CommaSeparatedIntegerField(CharField) - 字符串类型,格式必须为逗号分割的数字 UUIDField(Field) - 字符串类型,Django Admin以及ModelForm中提供对UUID格式的验证 FilePathField(Field) - 字符串,Django Admin以及ModelForm中提供读取文件夹下文件的功能 - 参数: path, 文件夹路径 match=None, 正则匹配 recursive=False, 递归下面的文件夹 allow_files=True, 允许文件 allow_folders=False, 允许文件夹 FileField(Field) - 字符串,路径保存在数据库,文件上传到指定目录 - 参数: upload_to = "" 上传文件的保存路径 storage = None 存储组件,默认django.core.files.storage.FileSystemStorage ImageField(FileField) - 字符串,路径保存在数据库,文件上传到指定目录 - 参数: upload_to = "" 上传文件的保存路径 storage = None 存储组件,默认django.core.files.storage.FileSystemStorage width_field=None, 上传图片的高度保存的数据库字段名(字符串) height_field=None 上传图片的宽度保存的数据库字段名(字符串) DateTimeField(DateField) - 日期+时间格式 YYYY-MM-DD HH:MM[:ss[.uuuuuu]][TZ] DateField(DateTimeCheckMixin, Field) - 日期格式 YYYY-MM-DD TimeField(DateTimeCheckMixin, Field) - 时间格式 HH:MM[:ss[.uuuuuu]] DurationField(Field) - 长整数,时间间隔,数据库中按照bigint存储,ORM中获取的值为datetime.timedelta类型 FloatField(Field) - 浮点型 DecimalField(Field) - 10进制小数 - 参数: max_digits,小数总长度 decimal_places,小数位长度 BinaryField(Field) - 二进制类型 字段合集
字段与数据库字段对应关系
对应关系: 'AutoField': 'integer AUTO_INCREMENT', 'BigAutoField': 'bigint AUTO_INCREMENT', 'BinaryField': 'longblob', 'BooleanField': 'bool', 'CharField': 'varchar(%(max_length)s)', 'CommaSeparatedIntegerField': 'varchar(%(max_length)s)', 'DateField': 'date', 'DateTimeField': 'datetime', 'DecimalField': 'numeric(%(max_digits)s, %(decimal_places)s)', 'DurationField': 'bigint', 'FileField': 'varchar(%(max_length)s)', 'FilePathField': 'varchar(%(max_length)s)', 'FloatField': 'double precision', 'IntegerField': 'integer', 'BigIntegerField': 'bigint', 'IPAddressField': 'char(15)', 'GenericIPAddressField': 'char(39)', 'NullBooleanField': 'bool', 'OneToOneField': 'integer', 'PositiveIntegerField': 'integer UNSIGNED', 'PositiveSmallIntegerField': 'smallint UNSIGNED', 'SlugField': 'varchar(%(max_length)s)', 'SmallIntegerField': 'smallint', 'TextField': 'longtext', 'TimeField': 'time', 'UUIDField': 'char(32)', ORM字段与MySQL字段对应关系
字段参数
字段参数 null 用于表示某个字段可以为空。 unique 如果设置为unique=True 则该字段在此表中必须是唯一的 。 db_index 如果db_index=True 则代表着为此字段设置索引。 default 为该字段设置默认值。 DateField和DateTimeField auto_now_add 配置auto_now_add=True,创建数据记录的时候会把当前时间添加到数据库。 auto_now 配置上auto_now=True,每次更新数据记录的时候会更新该字段。 关系字段 ForeignKey 外键类型在ORM中用来表示外键关联关系,一般把ForeignKey字段设置在 '一对多'中'多'的一方。 ForeignKey可以和其他表做关联关系同时也可以和自身做关联关系。 to 设置要关联的表 to_field 设置要关联的表的字段 on_delete 当删除关联表中的数据时,当前表与其关联的行的行为。 models.CASCADE 删除关联数据,与之关联也删除 db_constraint 是否在数据库中创建外键约束,默认为True。 字段参数 models.DO_NOTHING 删除关联数据,引发错误IntegrityError models.PROTECT 删除关联数据,引发错误ProtectedError models.SET_NULL 删除关联数据,与之关联的值设置为null(前提FK字段需要设置为可空) models.SET_DEFAULT 删除关联数据,与之关联的值设置为默认值(前提FK字段需要设置默认值) models.SET 删除关联数据, a. 与之关联的值设置为指定值,设置:models.SET(值) b. 与之关联的值设置为可执行对象的返回值,设置:models.SET(可执行对象) 字段参数
数据库查询优化
1 only与defer 2 select_related与prefetch_related 3 4 """ 5 orm语句的特点: 6 惰性查询 7 如果你仅仅只是书写了orm语句 在后面根本没有用到该语句所查询出来的参数 8 那么orm会自动识别 直接不执行 9 """ 10 # only与defer 11 # res = models.Book.objects.all() 12 # print(res) # 要用数据了才会走数据库 13 14 # 想要获取书籍表中所有数的名字 15 # res = models.Book.objects.values('title') 16 # for d in res: 17 # print(d.get('title')) 18 # 你给我实现获取到的是一个数据对象 然后点title就能够拿到书名 并且没有其他字段 19 # res = models.Book.objects.only('title') 20 # res = models.Book.objects.all() 21 # print(res) # <QuerySet [<Book: 三国演义爆款>, <Book: 红楼梦爆款>, <Book: 论语爆款>, <Book: 聊斋爆款>, <Book: 老子爆款>]> 22 # for i in res: 23 # print(i.title) # 点击only括号内的字段 不会走数据库 24 # print(i.price) # 点击only括号内没有的字段 会重新走数据库查询而all不需要走了 25 26 res = models.Book.objects.defer('title') # 对象除了没有title属性之外其他的都有 27 for i in res: 28 print(i.price) 29 """ 30 defer与only刚好相反 31 defer括号内放的字段不在查询出来的对象里面 查询该字段需要重新走数据 32 而如果查询的是非括号内的字段 则不需要走数据库了 33 34 """ 35 36 37 # select_related与prefetch_related 38 # select_related与prefetch_related 跟跨表操作有关 39 # res = models.Book.objects.all() 40 # for i in res: 41 # print(i.publish.name) # 每循环一次就要走一次数据库查询 42 43 # res = models.Book.objects.select_related('authors') # INNER JOIN 44 """ 45 select_related内部直接先将book与publish连起来 然后一次性将大表里面的所有数据 46 全部封装给查询出来的对象 47 这个时候对象无论是点击book表的数据还是publish的数据都无需再走数据库查询了 48 49 select_related括号内只能放外键字段 一对多 一对一 50 多对多也不行 51 52 """ 53 # for i in res: 54 # print(i.publish.name) # 每循环一次就要走一次数据库查询 55 56 res = models.Book.objects.prefetch_related('publish') # 子查询 57 """ 58 prefetch_related该方法内部其实就是子查询 59 将子查询查询出来的所有结果也给你封装到对象中 60 给你的感觉好像也是一次性搞定的 61 """ 62 for i in res: 63 print(i.publish.name)
今日考题
""" 今日考题 1.聚合查询,分组查询的关键字各是什么,各有什么特点或者注意事项 2.F与Q查询的功能,他们的导入语句是什么,针对Q有没有其他用法 3.列举常见的数据库字段及主要参数(越多越好) 4.orm数据库查询优化相关有哪些各有什么特点 """
今日内容回顾
在学习django orm的时候,最好自己复习一下MySQL相关的知识点,不要造成大面积的遗忘
聚合查询
# 单独使用的时候 需要借助于关键字 aggregate from django.db.models import Max,Min,Sum,Count,Avg aggregate(Max('age'),Min('price'),...)
分组查询
# 分组报错确保代码没有写错的情况下 需要去修改严格模式 annotate models.Book.objects.annotate() # models后面点什么就是按照上面分组 models.Book.objects.values('字段').annotate() # values出现在了annotate的前面,那么就按照values括号内指定的字段分组
F与Q查询
from django.db.models import F,Q # F查询:能够获取到表中指定字段对应的数据 1.库存大于卖出 2.所有的商品价格提高500块 3.所有商品名称后面加爆款 针对字符类型,在做拼接的时候需要额外的导入模块Concat,Value # Q查询:能够改变多个查询条件之间的关系 与或非 # 基本使用 filter(Q(),Q()) # and filter(Q()|Q()) # or filter(~Q()|Q()) # not # 高阶使用 """ 我们之前在查询数据的时候,条件的左边都是变量名的形式 如何做到左边也是字符串的形式呢?(动态的指定查询的条件) """ q = Q() # q默认多个条件之间还是and关系 q.connector = 'or' # 可以修改链接关系 q.children.append(('price',123)) q.children.append(('title','三国演义')) filter(q)
orm事务操作
""" 事务四大特性 ACID 数据库三大设计范式(自己百度搜索,自己整理) """ from django.db import transaction try: with transaction.atomic(): pass # with代码块里面所有的orm操作都属于同一个事务 except Exception as e: print(e)
常用字段
# 所有的字段都有一个verbose_name参数 用来对字段继续描述 AutoField -primary_key CharField -max_length IntegerField BigintegerField DecimalField -max_digits -decimal_places DateField DateTimeField -auto_now:修改 -auto_now_add:新建 EmailField BooleanField 传布尔值 存0/1 TextField 存大段文本(博客、日记、文章...) FileField -upload_to 可以指定文件的存放位置,该字段只存文件的路径 # 其他的字段 自己参考博客了解 # 外键字段及参数 to to_field on_delete db_index # 了解 一对一其实可以有两种指定方式 OneToOneField() ForeginKey(unique=True) 不推荐 # django不同 级联更新级联删除可能需要自己指定 ... # 还支持自定义字段 class MyCharField(models.Field): def __init__(...): ... def db_type(...): ... username = MyCharField(...)
数据库优化
# only与defer """ only 结果是一个列表套多个对象,这些对象默认只有only括号内的属性 但是也可以点击括号内没有的属性,但是需要额外的走数据库操作 defer 跟only刚好相反 对象里面唯独没有括号内指定的属性 """ # select_related与prefetch_related """ select_related内部的本质是联表操作 inner join 括号内只能放外键字段并且多对多不行 括号内可以放多个外键字段 select_related(外键字段1__外键字段2__外键字段3__...) 将联表之后的结果全部查询出来封装到对象里面 之后对象在点击表的字段的时候都无需再走数据库 prefetch_related内部本质是子查询 内部通过子查询的方式将多张的表数据也封装到对象中 这样用户在使用的时候也是感觉不出来的 上述两种方式,在不同的场景下效率各有千秋 """
作业
""" 1.整理今日内容到博客 2.熟练掌握数据库常用字段及参数以及应用场景 3.总结数据库查询优化的两组方法,尝试用自己的语言表述 4.完成图书管理系统 书籍表的增删改查(尽量将之前的知识点全部融入进去) """