Django的日常-模型层(2)

Django的日常-模型层(2)

几种常用的查询方式

聚合查询

​ 聚合,我们应该有这个概念,因为在数据库的SQL语言里面有聚合函数的概念,聚合,字面意思来说,就是把一类东西放在一起,但是跟分组又不太一样.

​ 我们知道我们在写SQL语句的时候,分组groupby和聚合函数是分不开的,因为一旦select语句里面有分组,那么我们就只能查询到分组依据的字段以及一些聚合函数组成的字段,别的字段写在select后面会报错的.

​ 所以,既然ORM是用来操作数据库的,那么我们在使用聚合查询的时候,就要遵循这个规律.聚合查询的具体用法如下:

# 首先导入聚合函数,聚合函数的关键字为aggregate
from django.db.models import Max,Min,Sum,Count,Avg # 大概格式为:models.表名.objects.查询方式(匹配条件).aggregate(聚合函数(表中的字段))
res = models.Book.objects.all().aggregate(Avg('price'))
print(res)

分组查询

​ 分组查询的关键字为annotate,使用起来非常的简单,实际在数据库里的操作其实就是groupby,分组查询会按照models后面的表名来分组,然后在后面括号内对分组后的数据进行操作

# 分组查询的格式为:models.表名.objects.annotate(对组内数据做操作)
res = models.Book.objects.annotate(author_number=Count('authors').values('author_num'))
print(res)

F和Q查询

​ 上面的聚合和分组查询都是我们从表中取出字段,然后与一个我们设定好的常量作比较,设想这样一种情景,如果我们需要从数据库取出来两个不同字段的值作比较,该如何实现呢?Django里面就给我们提供了F和Q查询来解决这一问题.

from django.db.models import F,Q
res = models.Book.objects.filter(sell = F('kucun'))
print(res)
# 以上语句可以实现在Book表中,我们以kucun列为依据,来查询得到自己想要的数据,甚至可以在查询的时候直接对数据做操作,比如
res = models.Book.objects.filter(sell = F('kucun')+100)
print(res) # 而Q查询比F查询更加强大,比如以下情景
# 我们需要根据不止一个条件来筛选记录,而且这些条件并不是and关系,而是or,或者not,此时就可以使用Q查询来解决问题,比如:
res = models.Book.objects.filter(Q(title='python')|Q(kucun=666))
res1 = models.Book.objects.filter(~Q(title='python')|Q(kucun=666))
print(res,res1)
# 以上两个例子,在两个Q之间用|是or的意思,即两者只要有一个成立就算符合条件,~则是取反的意思

​ Q查询还有一种更优秀的用法,我们知道Q查询其实是一个类,所以我们通过实例化产生一个对象,然后通过操作这个对象来实现Q查询以及对数据进行筛选的目的,比如

from django.db.models import Q
q = Q()
q.connector = 'or' # 这里可以直接配置其查询的方式,可以是or,可以是not
q.children.append(('title','python'))
q.children.append(('kucun',666))
res = models.Book.objects.filter(q)
print(res)

查询优化相关

​ ORM的查询优化一般会有四个比较常用的方法,他们都是成对出现,对比使用的,即only和defer,select_relater和prefetch_related,下面我们逐一介绍.

only和defer('字段')
首先他们的用法都是在关键字后面括号中加入表中有的字段.
only会将括号内字段对应的所有数据直接封装起来,然后返回给我们的对象中,因为only()最后返回的是一个对象,然后我们对这个对象来进行操作的时候,如果点出来的是之前括号内的字段,那么此次查询就不会再经过数据库,但是如果点出来是括号内不包含的字段,那么每次都会重新查询数据库.
做一个类比来说,only后面括号内字段所对应的数据相当于是拿出来放在了计算机的缓存中,我们在查询这个字段的时候就直接从缓存里取,而不需要再去查询数据库,可以节约大量查询的时间,当然这只是一个类比,实际情况并不是缓存的概念.
defer的用法就和only完全相反,defer后面括号内字段对应的数据,在每次查询的时候都需要查询数据库,而括号内不包含的字段对应的数据查询则不需要查询数据库.
个人理解only和defer的用法,only应该是用在不常修改的字段,也就是说only后面括号内的字段应该是修改次数尽量少的字段,这样既能提升效率,查询出来又不会出错.同理,defer后面括号内则是应该写频繁修改的字段,需要实时监控其变化的那种字段,比较合适.
select_relater和prefetch_related('外键关联表的表名')
首先,select_relater和prefetch_related的共同点在于其传参的规则是相同的,其后面的括号里面都只能放外键字段,且只能放一对一和一对多的外键字段,不能放多对多的字段,而且可以叠加,即可以select_related(外键字段__外键字段__外键字段)这样放.
select_related会自动帮我们做连表操作,这些表必须是有外键相关联的,select_related将连表之后的数据全部查询出来,然后封装起来,传给对象.
而prefetch_related看似也是一个连表操作,其实不是,prefetch_related实际上是一种类似子查询的存在,其会多次查询不同的表,然后把最后的结果返回出来.
这两种方法的区别在于
第一种,内部自动连表,消耗的资源和时间都在连表上,对数据库的操作比较少,数据库压力小
第二种,内部不做连表,消耗的资源和时间都在对数据库的查询上,数据库压力较大,但是整体效率较高

orm中常见字段

​ ORM中的字段就是我们在models.py里面定义表类型的时候,对于字段的定义所用的格式,常用的有以下几种:

# 自增的数字,常用来设置主键,primary_key = True就为主键
AutoField(primary_key = True) # 最常用的字符类型,相当于python中的varchar,max_length为其最大长度,超过这个长度的会被舍弃
CharField(max_length=32) # 整型,默认括号内为空
IntegerField() # 时间格式,常用参数有两个:
DateField()
auto_now:每次对对象进行操作都会更新当前时间
auto_now_add:在创建或添加对象的时候会添加这个时间,然后不会改变
# 小数格式的数字,max_digits为数字的总位数,decimal_places为数字的小数的位数
DecimalField(max_digits=8,decimal_places=2)
# 布尔类型,存储到数据库中的时候是0/1,并不是True和False
BooleanField()

choices参数

choices参数通常是IntegerField中的参数,因为我们市场希望的是用简单的数字来代替不同的意思,或者选项,比如我们在定义一个类中字段的是时候,该字段要显示用户的性别,我们可以这么写:

# models.py
choices = (
(1,'male'),
(2,'female'),
(3,'guess')
) sex = models.IntegerField(choices=choices)
# 以上这种操作就可以实现我们的需求,在后端向前端传送数据的时候只需要传1,2,3就可以了,大大减少了传送数据的量级

这种使用方式可以简化传送数据的大小,但是在取数据的时候也和原来有些区别,并不是直接用点的形式来取数据了.

from app01 import models
user_obj = models.Userinfo.objects.filter(pk=1),first()
res = user_obj.get_gender_display()
print(res)
# 其取值方式为固定句式,即
# 数据对象.get_字段名_display()
# 注意,如果数字没有对应关系的话,该种方式取出来的还是这个数字,并不会报错

orm的事务操作

​ 我们知道数据库中有事务,事务的四大特点ACID为

  1. 原子性

  2. 一致性

  3. 隔离性

  4. 持久性

    所以我们在ORM中也需要有事务的存在.具体操作如下

from django.db import transaction
with transaction.atomic()
#这里写的所有ORM语句都属于同一个事务
# 事务的结束标志就是这一个缩进
上一篇:通过history解决ajax不支持前进/后退/刷新


下一篇:Django中使用ORM