Django框架:8、聚合查询、分组查询、F与Q查询、ORM查询优化、ORM事务操作、ORM常用字段类型、ORM常用字段参数

Django 数据库

一、聚合查询

聚合函数:

  • Max
    • 求最大值
  • Min
    • 求最小值
  • Sun
    • 求和
  • Cont
    • 统计数量
  • Avg
    • 求平均值

使用方法

  • 类名.object.aggreate(聚合函数(‘字段名’))

​ 在MySQL数据库中,聚合函数需要在分组后(group by)才能使用

​ 在Django中,可以直接使用,需要搭配关键词:aggtegate

1、导入模块
	from django.db.models import Max,Min,Sun,Count,Avg
    
2、res = Book.objects.aggregate(Max('price'), Count('pk'), 最小价格=Min('price'), allPrice=Sum('price'),平均价格=Avg('price'))

二、分组查询

补充

问题:
	在执行orm分组查询中,如果报错,并且有关键字sql_mode strict mode
    
解决方法:
	移除sql_mode中的only_full_group_by

使用方法

  • 按表分组
    • models.表名.objects.annotate()
  • 按指定字段分组
    • models.表名.objects.values('字段名').annotate()

按表分组

# 分组查询
    # 统计每一本书的作者个数
    # res = models.Book.objects.annotate(author_num=Count('authors__pk')).values('title', 'author_num')
    # print(res)
    # 统计出每个出版社卖的最便宜的书的价格
    # res = models.Publish.objects.annotate(min_price=Min('book__price')).values('name', 'min_price')
    # print(res)
    # 统计不止一个作者的图书
    # 1.先统计每本书的作者个数
    # res = models.Book.objects.annotate(author_num=Count('authors__pk'))
    # 2.筛选出作者个数大于1的数据
    # res = models.Book.objects.annotate(author_num=Count('authors__pk')).filter(author_num__gt=1).values('title',
    #                                                                                                     'author_num')
    # print(res)
    # 查询每个作者出的书的总价格
    # res = models.Author.objects.annotate(总价=Sum('book__price'),count_book=Count('book__pk')).values('name','总价','count_book')
    # print(res)

按字段分组

res = models.Book.objects.values('publish_id').annotate(count_pk=Count('pk')).values('publish_id', 'count_pk')
    print(res)

三、F查询与Q查询

1、F查询

​ 当查询条件不是明确的,也需要从数据库中获取,就需要使用F查询,它可以直接对一整个字段下的数据进行操作

使用方法

1、导入模块
	from django.db.models import F
  
2、具体用法
	# 1.查询库存数大于卖出数的书籍
	# res  = models.Book.objects.filter(kucun__gt=F('maichu'))
	
    # 2.将所有书的价格涨800
    # models.Book.objects.update(price=F('price') + 800)	
    # 3.将所有书的名称后面追加爆款
from django.db.models.functions import Concat
from django.db.models import Value
models.Book.objects.update(title=Concat(F('title'), Value('新款')))

2、Q查询

​ ORM中,所有的数据条件都是使用逗号隔开,彼此的关系默认都是and的关系

​ Q:可以将多个查询条件的关系做修改

使用方法

1、导入模块
	from django.db.models import Q
    
2、具体用法
	# and关系
    models.Book.objects.filter(Q(pk=1),Q(title='三国'))  
    
    # or关系
    models.Book.objects.filter(Q(pk=1) | Q(title='三国'))
    
    # not关系
    models.Book.objects.filter(~Q(pk=1),Q(title='三国')) 

3、Q查询进阶操作

说明

​ 通过生成Q对象,并提前设置好条件,直接将Q对象传入filter中当作条件进行查询

具体用法

1、导入Q模块
	from django.db.models import Q
    
 2、产生Q对象
	q_obj = Q()
 
3、设置链接条件(默认为and,可以修改为or)
	q_obj.connector = 'or'

4、添加查询条件 (支持添加多个查询条件)
	# 查询条件1
	q_obj.children.append(('pk', 1))
 	# 查询条件2
	q_obj.children.append(('price__gt', 2000)) 
 
5、传入Q对象,进行查询(# 查询支持直接填写q对象)
	res = models.Book.objects.filter(q_obj) 
	print(res)

四、ORM查询优化

​ ORM查询优化是指,在我们使用的ORM语句进行查询时,它的底层帮助我们做了很多的优化,其目的都是为了节省内存空间

  • ORM默认都是惰性查询

    • 当我们不执行打印操作的时候,ORM语句不会执行,想要看到这个现象需要打开日志功能,即在配置文件中进行配置
  • ORM查询自带分页

    • 可以通过日志展示的代码查看,日志返回的sql代码后端会有一个limit
  • only与defer

    • 特别说明

1、only与defer

前置说明

​ 这里需要做一些具体的说明,方便大家理解only和defer。

​ 当我们在Django中执行ORM操作进行数据库查询的时候,其实内部的代码把所有的数据库中的记录,都封装到了ORM操作的对象中去了,因此我们可以通过点的方式或是索引等方式查询到对应的数据。

​ 但是当遇到查询的时候需要查询不在条件中的记录时,就需要执行sql语句进行查询了。

​ 比如我们在查询的时候,需要的结果在外键对应的表中,这时候去外键对应的表中查询数据,就需要执行sql语句进行查询,并且查询一条记录需要执行一次sql语句

​ 而我们的only的作用是把写在括号内的参数中的字段的值封装到对象中,让后续查找的时候 不需要执行sql语句进行查询,加快执行速度。或是起到一个减少代码封装的数据量,加快运行的作用。

​ 而defer则是和only相反,写在括号内的字段值不会被封装到对象中,别的字段反而会被封装到对象中。

only

​ 获取数据对象+含有指定字段对应的数据

res = models.Book.objects.only('title', 'price')
print(res)  # queryset [数据对象、数据对象]
for obj in res:
    print(obj.title)  # 点击括号内填写的字段 不走SQL查询
    print(obj.price)
    print(obj.publish_time)  # 可以点击括号内没有的字段获取数据 但是会走SQL查询

defer

res = models.Book.objects.defer('title', 'price')
print(res)  # queryset [数据对象、数据对象]
for obj in res:
    print(obj.title)  # 点击括号内填写的字段 走SQL查询
    print(obj.price)
    print(obj.publish_time)  # 点击括号内没有的字段获取数据 不走SQL查询

select_related与prefetch_related

​ 这里的两个方法分别对应连表查询和子查询,基本作用也是把数据从数据库封装到对象中,让查询效率更高。

# res = models.Book.objects.all()
# for obj in res:
#     print(obj.publish.name)  # 每次查询都需要走SQL
# res = models.Book.objects.select_related('authors')  # 先连表后查询封装
# res1 = models.Author.objects.select_related('author_detail')  # 括号内不支持多对多字段 其他两个都可以
# print(res1)
# for obj in res:
#     print(obj.publish.name)  # 不再走SQL查询

res = models.Book.objects.prefetch_related('publish')  # 子查询
for obj in res:
    print(obj.publish.name)

五、ORM事务操作

事务的四大特性(ACID)

​ 原子性、一致性、隔离性、持久性

相关SQL关键字

1、开启事务
    start transaction;
2、回滚
    rollback;
3、提交
    commit;
4、设置回滚节点
    savepoint;

相关重要概念

​ 脏读、幻读、不可重复读、MVCC多版本控制...

1、Django开启事务的方法

方法一:全局有效

# 配置文件的数据库相关配置中添加键值对	
	"ATOMIC_REQUESTS": True

方法二:装饰器

# 局部有效,这个视图函数内的一些orm操作属于一个事务
     from django.db import transaction
        @transaction.atomic
        def index():pass

方法三:上下文管理

# 局部有效,写在with下方的orm操作属于一个事务
    from django.db import transaction
    def reg():
        with transaction.atomic():
            pass

注意事项

​ 这里的三种方法有个小区别,前面两种方式执行事务,视图层函数遇到返回值类型不对,orm操作可以正常执行的,但是with上下文管理的方式操作事务的话,则不行,操作会回退。

六、ORM常用字段类型

1、默认字段类型

名称 含义
AutoField() Int自增列 必须填入参数 primary_key=True 当model中如果没有自增列 则自动会创建一个列名为id的列
CharField() 字符类型 必须提供max_length参数 max_length表示字符长度
IntegerField() 一个整数类型 范围在 -2147483648 to 2147483647 (一般不用它来存手机号(位数也不够) 直接用字符串存)
BigIntegerField() 长整型(有符号的) -9223372036854775808 ~ 9223372036854775807
DateField() 日期字段 日期格式 YYYY-MM-DD 相当于Python中的datetime.date()实例
DateTimeField() 日期时间字段 格式 YYYY-MM-DD HH:MM[:ss[.uuuuuu]][TZ] 相当于Python中的datetime.datetime()实例
DecimalField() 10进制小数 参数 max_digits 小数总长度 decimal_places,小数位长度
EmailField() 字符串类型 Django Admin以及ModelForm中提供验证机制
BooleanField() 布尔值类型 传布尔值存数字0或1
TextField() 文本类型 存储大段文本
FileField() 字符串 路径保存在数据库 文件上传到指定目录
参数 upload_to = " " 上传文件的保存路径
storage = None 存储组件 默认django.core.files.storage.FileSystemStorage
ForeignKey() 外键类型在ORM中用来表示外键关联关系 一般把ForeignKey字段设置在 '一对多’中’多’的一方 ForeignKey可以和其他表做关联关系同时也可以和自身做关联关系
OneToOneField() 一对一字段 通常一对一字段用来扩展已有字段 通俗的说就是一个人的所有信息不是放在一张表里面的,简单的信息一张表,隐私的信息另一张表,之间通过一对一外键关联
ManyToManyField() 简单来说就是在多对多表关系并且这一张多对多的关系表是有Django自动帮你建的情况下 下面的方法才可使用create add set remove clear

2、自定义字段类型

​ ORM支持用户自定义字段类型,比方说ORM中没有设置char类型的字段,我们可以使用自定义字段来实现他。

方法

class MyCharField(models.Field):
    def __init__(self, max_length, *args, **kwargs):
		self.max_length = max_length
		uper().__init__(max_length=max_length, *args, **kwargs)

	def db_type(self, connection):
		return 'char(%s)' % self.max_length


# 这里是调用我们定义的字段类型
class User(models.Model):
    name = models.CharField(max_length=32)
    info = MyCharField(max_length=64)

七、ORM常用字段参数

名称 含义
primary_key 主键
verbose_name 注释
max_length 字段长度
max_digits 小数总共多少位
decimal_places 小数点后面的位数
auto_now 每次操作数据自动更新事件
auto_now_add 首次创建自动更新事件后续不自动更新
null 允许字段为空
default 字段默认值
unique 唯一值
db_index 给字段添加索引
choices 当某个字段的可能性能够被列举完全的情况下使用。如:性别、学历、工作状态、...
to 关联表
to_field 关联字段(不写默认关联数据主键)
on_delete 当删除关联表中的数据时,当前表与其关联的行的行为。

on_delete

​ 当两张连表进行关联后,主表的数据被删除时,从表中对应的数据正在默认情况下也会被删除,但是on_delete可以设置默认值,当主表数据被删除时,从表中对应的数据会被修改为默认值

def func():
    return 10

class MyModel(models.Model):
    user = models.ForeignKey(
                    to="User",
                    to_field="id",
                    on_delete=models.SET(func)
                    )   
 # on_delete可以设置默认值,当主表数据被删除时,从表中对应的数据会被修改为默认值


不同值对应的功能:
1、models.CASCADE
	级联操作,当主表中被连接的一条数据删除时,从表中所有与之关联的数据同时被删除

2、models.SET_NULL
    当主表中的一行数据删除时,从表中所有与之关联的数据的相关字段设置为null,此时注意定义外键时,这个字段必须可以允许为空
    
3、models.PROTECT
    当主表中的一行数据删除时,由于从表中相关字段是受保护的外键,所以都不允许删除
    
4、models.SET_DEFAULT
    当主表中的一行数据删除时,从表中所有相关的数据的关联字段设置为默认值,此时注意定义外键时,这个外键字段应该有一个默认值
    
5、models.SET()
    当主表中的一条数据删除时,从表中所有的关联数据字段设置为SET()中设置的值,与models.SET_DEFAULT相似,只不过此时从表中的相关字段不需要设置default参数
       
6、models.DO_NOTHING
    什么都不做,一切都看数据库级别的约束,注数据库级别的默认约束为RESTRICT,这个约束与django中的models.PROTECT相似

choices

​ 当字段数据的可能性是可以完全列举出来的时候 应该考虑使用该参数

  • get_gender_display()
    • 获取choices参数内的数据值
class UserInfo(models.Model):
    username = models.CharField(max_length=32)
    gender_choice = (
        (1, '男性'),
        (2, '女性'),
        (3, 'other'),
    )
    gender = models.IntegerField(choices=gender_choice)

user_obj = models.UserInfo.objects.filter(pk=1).first()
print(user_obj.gender)  # 获取的是真实数据
print(user_obj.get_gender_display())
user_obj1 = models.UserInfo.objects.filter(pk=2).first()
user_obj2 = models.UserInfo.objects.filter(pk=3).first()
user_obj3 = models.UserInfo.objects.filter(pk=4).first()
print(user_obj1.get_gender_display())
print(user_obj2.get_gender_display())
print(user_obj3.get_gender_display())   # 如果没有则按照真实数据返回

image-20221219202307836

posted @ 2022-12-19 21:42  kangshong  阅读(133)  评论(0编辑  收藏  举报