ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

day65——聚合函数、分组查询、F与Q查询、django开事务、orm查询优化

2020-06-01 22:53:48  阅读:248  来源: 互联网

标签:__ models res price django Book orm 查询


聚合函数(Max,Min,Sum,Avg,Count)

Max最大值、Min最小值、Sum求和、Avg求平均值、Count统计个数。

聚合查询通常在分组之后使用,如果想单独使用需要用aggregate将聚合函数括起来,使用之前需要先导入函数

from django.db.models import Mix,Min,Sum,Avg,Count

# 1.书籍表中书的平均价格
res = models.Book.objects.aggregate(Avg('price'))
print(res)
# {'price__avg': 377.91}

# 2.聚合函数组合使用
models.Book.objects.aggregate(Max('price'),Min('price'),Sum('price'),Count('pk'),Avg('price'))
print(res)
"""
{'price__max': Decimal('666.11'), 'price__min': Decimal('123.11'), 
'price__sum': Decimal('1889.55'), 'pk__count': 5, 'price__avg': 377.91}"""

只要是跟数据库相关的模块基本都在django.db.models里面,这里面没有就在django.db里面

分组查询

分组查询的关键字是annotate

1.统计每一本书的作者个数

from django.db.models import Mix, Min, Sum, Avg, Count
# 方式1
res = models.Book.objects.annocate(author_num=Count('authors__pk')).values('title','authors_num')
# 方式2 简写
res1 = models.Book.objects.annocate(author_num=Count('authors')).values('title','authors_num')
print(res1)

models后面点什么表就是按照什么表的主键进行分组,也就是按书表的主键进行分组;author_num是我们自定义的字段,用来存储聚合函数Count统计出来的结果,就是每本书对应的作者个数。是通过作者数据的主键值进行统计的,orm还支持我们简写,直接写字段自动对主键进行统计。Count括号内用到了正向跨表查询,直接点外键字段由Book表跨到Author表,然后__pk取主键值。

2.统计每个出版社卖的最便宜的书的价格

res = models.Publish.objects.annotate(min_price = Min('book__price')).values('book__title','min_price')
print(res)

首先按出版社进行分组,反向查询点表名小写跨到Book表内,Min函数从中查询出每个出版社出版的最便宜书的价格,并自定义一个min_price来存储查询的结果,最后values内取出想要的字段的值即可。

3.统计不止一个作者的图书,已经图书的作者个数

res = models.Book.objects.annotate(author_num = Count('author')).filter(author_num__gt=1).values('title','author_num')
print(res)

可分为两步:

  1. 先按照图书进行分组,求每本书对应的作者个数。
  2. 进一步过滤出不止一个作者的图书。
# 第一步
models.Book.objects.annotate(author_num=Count('author'))

# 第二步
models.Book.objects.annotate(author_num = Count('author')).filter(author_num__gt=1)

# 取值
res = models.Book.objects.annotate(author_num = Count('author')).filter(author_num__gt=1).values('title','author_num')
    print(res)

4.查询每个作者出的书的总价格

res = models.Author.objects.annotate(sum_price=Sum('book__price')).values('name', 'sum_price')
print(res)

首先按作者进行分组,反向查询点表名小写跨到Book表内,Sum函数计算出每个作者出过的书的总价格,并自定义一个sum_price来存储结果,最后values内取出想要的字段的值即可。

如果我想按照指定的字段分组该如何处理呢?

annotate前加values(),在values括号内指定分组的依据

models.Book.objects.values.('price').annotate()  # 按照书籍表的price字段进行分组。

F与Q查询

F查询

首先在Book表中增加两条字段库存(reserve)和卖出(sales)

reserve = models.IntegerField(default= 1000)
sales =  models.IntegerField(default= 1000)

手动的去数据库中将两个字段的值修改一下,改成满足下面题目的数据

sales reserve title
2000 500 三国演义
500 1000 红楼梦
10 990 论语
1000 1000 聊斋
800 1200 庄子

能够帮你直接获取到表中的某个字段对应的数据

1.查询卖出数大于库存数的书籍

题目中的筛选条件不在数具体的数据了,而是一个字段,需要我们将卖出数和库存数进行比较,这个时候就需要用到F查询了。

F查询能够帮助你直接获取到表中某个字段对应的数据,需要提前导入。

from django.db.models import F
res = models.Book.objects.filter(sales__gt=F('reserve')).values('title')
print(res)
# <QuerySet [{'title': '三国演义'}]>

2.将所有书籍的价格提升500块

models.Book.objects.update(price=F('price')+500)

3.将所有书的名称后面加上畅销两个字

在操作字符类型的数据的时候,F不能直接做到字符串的拼接,还需要导入两个方法才能完成拼接字符。

from django.db.models.functions import Concat
from django.db.models import values
models.Book.objects.update(title=Concat(F('title'), Value('畅销')))
"""
三国演义畅销
红楼梦畅销
论语畅销
聊斋畅销
庄子畅销 
"""

models.Book.objects.update(title=F('title') + 畅销')
# 不借助两个方法直接拼接会导致所有的名称变成零
"""
title
0
0
0
0
0
"""

Q查询

1.查询卖出数大于100或者价格小于600的书籍

from django.db.models import Q

res  = models.Book.objects.filter(maichu__gt=100,price__lt=600)
print(res)  # <QuerySet []>  filter括号内逗号隔开的各个条件是and关系

res = models.Book.objects.filter(Q(sales__gt=100),Q(price__lt=600))
# print(res.query)  # 查看sql语句条件是and与关系
print(res)  # <QuerySet []> 

res = models.Book.objects.filter(Q(sales__gt=100)|Q(price__lt=600)) # 将逗号改成|,可以将条件改成or或关系
# print(res.query)
print(res)
"""
<QuerySet [{'title': '三国演义'}, {'title': '红楼梦'},
{'title': '聊斋'}, {'title': '庄子'}]>"""

# 补充 ~ 非
res = models.Book.objects.filter(~Q(sales__gt=100)|Q(price__lt=600)).values('title')
# print(res.query)
print(res)  # <QuerySet [{'title': '论语'}]>

Q查询高阶用法

够将查询条件的左边可以是字符串的形式

q = Q()
q.connector ='or'  # 将多个条件的连接关系改成or或关系
q.children.append(('sales__gt',100))
q.children.append(('price__lt',600))
res = models.Book.objects.filter(q).values('title')
print(res)
"""
<QuerySet [{'title': '三国演义'}, {'title': '红楼梦'}, 
{'title': '聊斋'}, {'title': '庄子'}]>"""

django中如何开启事务

事务的四个特性ACID:

  • A原子性:不可分割的最小单位
  • C一致性:跟原子性是相辅相成,要么同时成功,要么同时失败
  • I隔离性:事务之间互相不干扰
  • 持久性:事务一旦确认永久生效

事务回滚:rollback

事务确认:commit

from django.db import transaciton
with transaction.atomic():
    # sql1
    # sql2
    ...
    # 在with代码快内书写的所有orm操作都是属于同一个事务
    
#处理报错增加异常捕获
from django.db import transaction
try:
    with transaction.atomic():
        ...
except Exception as e:
	print(e)
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)					- 文本类型
	-该字段可以用来存大段内容(文章、博客...)  没有字数限制

FileField(Field)					- 字符类型
	-upload_to = "/data"
    给该字段传一个文件对象,会自动将文件保存到/data目录下然后将文件路径保     存到数据库中/data/a.txt
 

外键字段及参数补充

ForeignKey(unique=True)    ===    OneToOneField()  #两者等价
# 你在用前面字段创建一对一 orm会有一个提示信息 orm推荐你使用后者但是前者也能用

db_index
	-如果db_index=True 则代表着为此字段设置索引

"""
django2.X及以上版本 需要你自己指定外键字段的级联更新级联删除:
"""
on_delete = True

orm字段

orm自定义字段

# 定义
from django.db.models import Field
class MyCharField(Field):
    def __init__(self, max_length, *args, **kwargs):
        # 调用父类的init方法
        super().__init__(max_length=max_length, *args, **kwargs) # 一定要是关键字的形式传入 
        self.max_length = max_length

    def db_type(self, connection):
        """
        返回真正的数据类型及各种约束条件
        :param connection:
        :return:
        """
        return f'char({self.max_length})'
    
# 使用
myfield = MyCharField(max_length=16,null=True)

数据库查询优化

orm语句的特点

惰性查询:如果只是单单书写orm语句,而在后面的代码中根本没有使用到该语句的结果,那么orm会自动识别,将不会执行没有使用的语句。

res = models.Book.objects.all()  # 没有使用,不走数据库

only与defer

only:查询的对象中只获取only()括号内的字段,对象的其他字段不拿

代码体现:

res = models.Book.objects.only('title')  # 查询的对象中只有title字段数据,没有其他字段数据
for i in res:
    print(i.title)  # 配置sql日志后可以看到,循环打印title不再走数据库
    print(i.price)  # 打印其他字段时,查询到的对象中没有,循环打印还需去数据库中相继查询字段的数据  

defe:查询的对象除了defer()括号内的字段,其他字段全部获取

代码体现:

res = models.Book.objects.defer('title')  # 查询的对象中只有title字段数据没有,而其他字段数据都有
for i in res:
    print(i.title)  # 查询到的对象中没有title字段,循环打印还需去数据库中相继查询字段的数据  
    print(i.price)  # 打印其他字段时,查询到的对象中有字段数据无需走数据库

select_related(联表):类似于数据库sql语句的联表操作,先将两张表以内连接inner join的方式连接成一张虚拟的大表,然后一次性将虚拟表里面的所有数据全部封装给查询出来的对象,这样无论是取左表的数据还是取右边的数据都不需要去数据库中再次的查询了。

代码体现:

res = models.Book.objects.all()
for i in res:
	print(i.publish.name)  # 每循环一次就要走一次数据库查询
    
res = models.Book.objects.select_related('publish')
for i in res:
	print(i.publish.name)  # 每循环时无需去数据库中查询,封装给 对象的虚拟表中有Book表和Publish公有的数据。 

prefetch_related(子查询):该方法内部其实就是子查询,只不过它将子查询的所有结果也封装到对象里面去了,表面上时一次就查询完了,实际内部也是分步进行了子查询。

res = models.Book.objects.prefetch_related('publish')
for i in res:
        print(i.publish.name)

标签:__,models,res,price,django,Book,orm,查询
来源: https://www.cnblogs.com/zhangtieshan/p/13028084.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有