ICode9

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

正反向查询进阶操作、聚合查询、分组查询、F与Q查询、ORM查询优化

2022-09-06 20:32:43  阅读:265  来源: 互联网

标签:进阶 objects models res 查询 ORM values print


目录

正反向查询进阶操作

    '''正反向查询进阶操作'''
    # 1.查询主键为1的书籍对应的出版社名称及书名
    res = models.Publish.objects.filter(book__pk=1).values('name', 'book__title')
    print(res)  # <QuerySet [{'name': '清华大学出版社', 'book__title': '女人你成功引起了我的注意'}]>

    # 2.查询主键为3的书籍对应的作者姓名及书名
    res1 = models.Author.objects.filter(book__pk=3).values('name', 'book__title')
    print(res1)  # <QuerySet [{'name': 'jfw', 'book__title': '凉生,我们可不可以不忧伤'}]>

    # 3.查询zxb的作者的电话号码和地址
    res2 = models.AuthorDetail.objects.filter(author__name='zxb').values('phone', 'addr')
    print(res2)  # <QuerySet [{'phone': 119, 'addr': '浙江'}]>

    # 4.查询东方出版社出版的书籍名称和价格
    res4 = models.Book.objects.filter(publish__name='东方出版社').values('title', 'price')
    print(res4)  # <QuerySet [{'title': '桃疆', 'price': Decimal('14.67')}, {'title': '三生三十十里桃花', 'price': Decimal('35.98')}, {'title': '玄霜', 'price': Decimal('54.21')}]>

    # 5.查询zxr写过的书的名称和日期
    res5 = models.Book.objects.filter(author__name='zxb').values('title', 'publish_time')
    print(res5)  # <QuerySet [{'title': '女人你成功引起了我的注意', 'publish_time': datetime.datetime(2022, 9, 5, 13, 16, 43, 303992, tzinfo=<UTC>)}]>

    # 6.查询电话是110的作者姓名和年龄
    res6 = models.Author.objects.filter(author_detail__phone=110).values('name', 'age')
    print(res6)  # <QuerySet [{'name': 'zxr', 'age': 18}]>

    # 7.查询主键为1的书籍对应的作者电话号码
    res7 = models.AuthorDetail.objects.filter(author__book__pk=1).values('phone')
    print(res7)  # <QuerySet [{'phone': 120}, {'phone': 119}]>
    res8 = models.Author.objects.filter(book__pk=1).values('author_detail__phone')
    print(res8)  # <QuerySet [{'author_detail__phone': 120}, {'author_detail__phone': 119}]>

聚合查询

聚合函数:max、min、sum、avg、count

1.使用聚合函数之前需要导入模块

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

2.聚合函数的使用

聚合函数通常情况下是配合分组一起使用的

3.关键字aggregate

没有分组之前如果单纯的使用聚合函数 需要关键字aggregate

# 1.查询所有书的平均价格
res = models.Book.objects.aggregate(Avg('price'))
print(res)  # {'price__avg': Decimal('183.117778')}

image

res1 = models.Book.objects.aggregate(Max('price'), Min('price'), Sum('price'), Avg('price'), Count('pk'))
print(res1)

image

分组查询

image
报错原因
image

打开MySQL
输入>>>:show variables like '%mode%';

分组有一个特性 默认只能够直接获取分组的字段 其他字段需要使用方法
我们也可以忽略掉该特性 将sql_mode中only_full_group_by配置移除即可
# 示例1:统计每一本书的作者个数
res = models.Book.objects.annotate(author_num=Count('author__pk')).values('title', 'author_num')
print(res)

"""
    1.按照整条数据分组
        models.Book.objects.annotate()  按照一条条书籍记录分组
    2.按照表中某个字段分组()
        models.Book.objects.values('title').annotate()  按照annotate之前values括号中指定的字段分组
"""

"""
<QuerySet [{'title': '女人你成功引起了我的注意爆款~爆款', 'author_num': 2},
{'title': '凉生,我们可不可以不忧伤爆款~爆款', 'author_num': 1},
{'title': '桃疆爆款~爆款', 'author_num': 2},
{'title': '我是你的,生生世世爆款~爆款', 'author_num': 2},
{'title': '国民大侦探爆款~爆款', 'author_num': 1},
{'title': '十宗罪爆款~爆款', 'author_num': 1},
{'title': '三生三十十里桃花爆款~爆款', 'author_num': 1},
{'title': '玄霜爆款~爆款', 'author_num': 1},
{'title': '一生一世爆款~爆款', 'author_num': 1}]>
"""

1.分组查询

annotate()为调用的QuerySet中每一个对象都生成一个独立的统计值(统计方法用聚合函数,所有使用前要从django.db.models引入Avg,Max,Count,Sum(首字母大写))。

2.返回值

分组后,用values取值,则返回值是QuerySet书籍类型里面为一个个字典
分组后,用values_list取值,则返回值是QuerySet数据类型里面为一个个元组

MySQL中的limit相当于ORM中的QuerySet数据类型的切片。

3.分组查询关键字

annotate()  里面放聚合函数

1.values 或者 values_list 放在 annotate 前面:values 或者 values_list 是声明以什么字段分组,annotate 执行分组。
2.values 或者 values_list 放在annotate后面: annotate 表示直接以当前表的pk执行分组,values 或者 values_list 表示查询哪些字段, 并且要将 annotate 里的聚合函数起别名,在 values 或者 values_list 里写其别名。
3.filter放在 annotate 前面:表示where条件
4.filter放在annotate后面:表示having

跨表分组查询本质就是将关联表join成一张表,再按单表的思路进行分组查询

分组练习题

models后面点什么 就按什么分组 

author_num 是我们自己定义的字段 用来存储统计出来的每本书对应的作者个数

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

res = models.Book.objects.annotate(author_num=Count('author__pk')).values('title', 'author_num')
print(res)
res1 = models.Book.objects.values('publish_id').annotate(book_num=Count('pk')).values('publish_id','book_num')
print(res1)
"""
    1.按照整条数据分组
        models.Book.objects.annotate()  按照一条条书籍记录分组
    2.按照表中某个字段分组()
        models.Book.objects.values('title').annotate()  按照annotate之前values括号中指定的字段分组
"""
"""
<QuerySet [{'title': '女人你成功引起了我的注意', 'author_num': 2},
{'title': '凉生,我们可不可以不忧伤', 'author_num': 1},
{'title': '桃疆', 'author_num': 2},
{'title': '我是你的,生生世世', 'author_num': 2},
{'title': '国民大侦探', 'author_num': 1},
{'title': '十宗罪', 'author_num': 1},
{'title': '三生三十十里桃花', 'author_num': 1},
{'title': '玄霜', 'author_num': 1},
{'title': '一生一世', 'author_num': 1}]>
"""

image

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

res1 = models.Publish.objects.annotate(min_price=Min('book__price')).values('name', 'min_price')
print(res1)
"""
<QuerySet [{'name': '清华大学出版社',
'min_price': Decimal('28.98')},
{'name': '北方出版社', 'min_price': Decimal('34.98')},
{'name': '南方出版社', 'min_price': Decimal('56.29')},
{'name': '东方出版社', 'min_price': Decimal('14.67')}]>
"""

image
3.统计不止一个作者的图书

'''filter在annotate前面则是where 在annotate后面则是having'''
res = models.Book.objects.annotate(author_num=Count('author__pk')).filter(author_num__gt=1).values('title', 'author_num')
print(res)

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

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

"""
<QuerySet [{'name': 'zxr', 'book_sum_price': Decimal('159.25')},
{'name': 'wyn', 'book_sum_price': Decimal('1365.63')},
{'name': 'zxb', 'book_sum_price': Decimal('1383.86')},
{'name': 'jfw', 'book_sum_price': Decimal('103.95')}]>
"""

image

F与Q查询

F查询

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

注意:
在操作字符串类型的数据的时候, F不能够直接做到字符串的拼接

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

res = models.Book.objects.filter(storage_num__gt=F('sale_num'))
print(res)

image
将所有书籍的价格上涨1000快

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

image
image
给所有书籍名称后面加上爆款后缀
针对字符串数据无法直接拼接
image

models.Book.objects.filter().all().update(title=F('title') + '爆款')  # 针对字符串数据无法直接拼接
    # 导入Concat模块
    from django.db.models.functions import Concat
    # 导入Value模块
    from django.db.models import Value
    models.Book.objects.filter().all().update(title=Concat(F('title'), Value('~爆款')))

image

Q查询

可以改变filter括号内多个条件之间的逻辑运算符,还可以将查询的结果改为字符串形式

filter()等方法中的关键字参数查询都是一起进行"and",如果你需要执行更复杂的查询(列如ORM语句),你可以使用Q对象。

查询卖出数大于100并且价格小于60的书籍

res = models.Book.objects.filter(Q(sale_num__gt=100), Q(price__lt=600))
print(res)

image

查询卖出数大于130或者价格小于30的书籍

res = models.Book.objects.filter(Q(sale_num__gt=130) | Q(price__lt=30))
print(res)

image

查询卖出数不是大于80或者价格大于600的书籍

res = models.Book.objects.filter(~Q(sale_num__gt=80) | Q(price__gt=600))
print(res)

image
总结

,               and关系
|               or关系
~               not关系

Q 默认是and,可以进行修改
    q = Q()
    q.connector = 'or'

Q查询的进阶用法

可以将查询的结果改为字符串形式

先产生一个对象
    q = Q()
修改默认的对象
    q.connector = 'or'
添加查询条件,第一个写一个字符串的字段名 第二个元素写一些具体的数值
    q.children.append(('pk', 1))
    q.children.append(('publish_id', 3))
添加好以后q对象支持直接用filter进行筛选
    res = models.Book.objects.filter(q)
    print(res)

image

image

ORM查询优化

django orm默认都是惰性查询
    当orm的语句在后续的代码中真正需要使用的时候才会执行
django orm自带limit分页
    减轻数据库端以及服务端的压力

1.ORM查询优化之only(单表)

res = models.Book.objects.only('title','price')
for obj in res:
     print(obj.title)
     print(obj.price)
     print(obj.publish_time)
    """
    only会将括号内填写的字段封装成一个个数据对象 对象在点击的时候不会再走数据库查询
    但是对象也可以点击括号内没有的字段 只不过每次都会走数据库查询 
    如果看到有人只有only查询的话 尽量不要去点括号内没有的字段 因为它每次都会去查询一次
    """

image
image

2.ORM查询优化之defer(单表)

res = models.Book.objects.defer('title', 'price')
for obj in res:
    # print(obj.title)
    # print(obj.price)
    print(obj.publish_time)
    """
    defer与only刚好相反
        数据对象点击括号内出现的字段 每次都会走数据库查询
        数据对象点击括号内没有的字典 不会走数据库查询
    """

image

3.ORM查询优化之select_related(多表)

res = models.Book.objects.all()
for obj in res:
     print(obj.publish.name)  # 频繁走数据库查询

res = models.Book.objects.select_related('publish')
"""
    select_related括号内只能接收外键字段(一对多 一对一) 它会在内部自动拼表 得出的数据对象在点击表中数据的时候都不会再走数据库查询
"""

image
image

4.ORM查询优化之prefetch_related(多表)

for obj in res:
     print(obj.publish.name)
"""
    prefetch_related底层其实是子查询 将查询之后的结果也一次性封装到数据对象中 用户在使用的时候是感觉不出来的
"""
res = models.Book.objects.prefetch_related('publish')
for obj in res:
    print(obj.publish.name)

image

额外补充个知识:当表中已经有数据的情况下 添加新的字段需要指定一些参数

class Book(models.Model):
    title = models.CharField(max_length=32)
    price = models.DecimalField(max_digits=8, decimal_places=2)
    publish_time = models.DateTimeField(auto_now=True)

    publish = models.ForeignKey(to='Publish', on_delete=models.CASCADE)
    author = models.ManyToManyField(to='Author')
    '''
    当表中已经有数据的情况下 添加新的字段需要指定一些参数
        1.设置字段值存于为空    null=True
        2.设置字段默认值    default=1000
        3.在终端直接给出默认值
    '''
    storage_num = models.IntegerField(verbose_name='库存数', null=True)
    sale_num = models.IntegerField(verbose_name='卖出数目', default=1000)

    def __str__(self):
        return f'对象:{self.title}'

报错问题

django.db.utils.OperationalError: (2002, "Can't connect to server on '127.0.0.1' (10061)")
image
解决方法:查看数据库是否启动,导致这个错误很可能是MySQL停止了服务

标签:进阶,objects,models,res,查询,ORM,values,print
来源: https://www.cnblogs.com/zxr1002/p/16663198.html

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

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

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

ICode9版权所有