Django 聚合与查询集API实现侧边栏

本文从Django官方文档总结而来,将聚合的主要用法和查询集的常见方法做一归纳。

聚合

1. 聚合的产生来源于django数据库查询,通常我们使用django查询来完成增删查改,但是有时候需要更复杂的方法才能完成对数据的提取、筛选、更改,所以需要一组对象聚合来完成这种操作。模型举例如下:

from django.db import models

class Author(models.Model):
    name = models.CharField(max_length=100)
    age = models.IntegerField()

class Publisher(models.Model):
    name = models.CharField(max_length=300)
    num_awards = models.IntegerField()

class Book(models.Model):
    name = models.CharField(max_length=300)
    pages = models.IntegerField()
    price = models.DecimalField(max_digits=10, decimal_places=2)
    rating = models.FloatField()
    authors = models.ManyToManyField(Author)
    publisher = models.ForeignKey(Publisher)
    pubdate = models.DateField()

class Store(models.Model):
    name = models.CharField(max_length=300)
    books = models.ManyToManyField(Book)
    registered_users = models.PositiveIntegerField()

 根据给出的模型,先引入三个例子:

# Total number of books.
>>> Book.objects.count()
2452

# Total number of books with publisher=BaloneyPress
>>> Book.objects.filter(publisher__name='BaloneyPress').count()
73
>>> from django.db.models import Avg
>>> Book.objects.all().aggregate(Avg('price'))
{'price__avg': 34.35}

注意几个点: .all() 和 .filter()    和  .count() 和 .aggregate() 为方法,前两者是返回新的查询集的方法(括号里面有参数),后两者是不返回查询集的方法 。 publisher 显然为字段名, name为字段查找,两者之间以双下划线连接 :__       price也是字段名,Avg 为聚合函数,用来求平均值。 以上提及的方法字段查找聚合函数将在查询集API中介绍。那么,我们先介绍聚合。

2.django提供了两种生成聚合的方法

1)从整个查询集生成统计值,主要用法:aggregate(*args, **kwargs)

 aggregate()是QuerySet 的一个终止子句,也就是说aggregate返回一个字典,包含根据QuerySet 计算得到的聚合值(平均数、和等等)。aggregate() 的每个参数指定返回的字典中将要包含的值。eg:

Book.objects.all()  # 返回所有图书的集合

>>> from django.db.models import Avg  # 引入用来求平均值的聚合函数 Avg
>>> Book.objects.all().aggregate(Avg('price'))  # 要计算所有书的平均价格,通过在查询集后面附加aggregate()子句实现
{'price__avg': 34.35} # 返回的是字典

>>> Book.objects.aggregate(Avg('price'))  # all()在这里多余,可以省掉
{'price__avg': 34.35}                     # 返回的字典中,键为聚合值的标识符,由字段和聚合函数的名称自动生成 ,值为计算出来的聚合值

>>> Book.objects.aggregate(average_price=Avg('price'))
 {'average_price': 34.35}                 # 为聚合值更换名称,提供参数average_price
>>> from django.db.models import Avg, Max, Min   # 生成了不止一个聚合
>>> Book.objects.aggregate(Avg('price'), Max('price'), Min('price')) 
{'price__avg': 34.35, 'price__max': Decimal('81.20'), 'price__min': Decimal('12.99')}

2)为查询集的每一项成聚合,主要用法:annotate(*args, **kwargs)

这种方法为每一个对象都生成一个独立的汇总值,比如,如果你在检索一列图书,你可能想知道每一本书有多少作者参与。每本书和作者是多对多的关系。我们想要汇总QuerySet.中每本书里的这种关系。逐个对象的汇总结果可以由annotate()子句生成。当annotate()子句被指定之后,QuerySet中的每个对象都会被注上特定的值。这些注解的语法都和aggregate()子句所使用的相同。annotate()的每个参数都描述了将要被计算的聚合。

# Build an annotated queryset
>>> from django.db.models import Count
>>> q = Book.objects.annotate(Count('authors')) # 和aggregate语法相同,不同的是annotate返回的q是各个对象,可用q[0],q[1]等取出对象
# Interrogate the first object in the queryset
>>> q[0]  # 返回的是第一个Book对象
<Book: The Definitive Guide to Django>
>>> q[0].authors__count   # 编写第一本书的作者数目为2, authors为Book模型中的字段名,count为字段查询,所以用双下划线连接
2
# Interrogate the second object in the queryset
>>> q[1]  # 返回的是第二个Book对象
<Book: Practical Django Projects>
>>> q[1].authors__count
1

>>> q = Book.objects.annotate(num_authors=Count('authors'))  # 提供了自定义的num_authors别名代替了authors__count
>>> q[0].num_authors
2
>>> q[1].num_authors

1

与 aggregate() 不同的是, annotate() 不是一个终止子句。annotate()子句的返回结果是一个查询集 (QuerySet);这个 QuerySet可以用任何QuerySet方法进行修改,包括 filter(), order_by()。发现aggregate 和 annotate用法的区别了吗,再次举例如下(在聚合函数中指定聚合字段时,Django 允许你使用同样的 双下划线 表示关联关系,):

>>> from django.db.models import Max, Min
>>> Store.objects.annotate(min_price=Min('books__price'), max_price=Max('books__price'))
# 查找每个商店提供的图书的价格范围

>>> Store.objects.aggregate(min_price=Min('books__price'), max_price=Max('books__price'))
# 查找所有书店中最便宜的书和最贵的书的价格

>>> Store.objects.aggregate(youngest_age=Min('books__authors__age'))
# 利用双下划线延伸关系链,查找所有书店中的所有作者的最小年龄

3. 聚合和其他查询子句

filter() 和 exclude()

>>> from django.db.models import Count, Avg
>>> Book.objects.filter(name__startswith="Django").annotate(num_authors=Count('authors'))
# 使用annotate() 子句时,过滤器有限制注解对象的作用。例如,得到每本以 "Django" 为书名开头的图书作者的总数

>>> Book.objects.filter(name__startswith="Django").aggregate(Avg('price'))
# 使用aggregate()子句时,过滤器有限制聚合对象的作用。例如,算出所有以 "Django" 为书名开头的图书平均价格
>>> Book.objects.annotate(num_authors=Count('authors')).filter(num_authors__gt=1)
# 得到不止一个作者的图书

注意以上annotate() 和 filter()子句的顺序,顺序不同查询结果也会不同(后者筛选的出版商为前者的子集。):

>>> Publisher.objects.annotate(num_books=Count('book')).filter(book__rating__gt=3.0)
# 返回了至少出版了一本好书(评分大于 3 分)的出版商, 在这些出版商中包含出版商所发行的所有图书!(这些出版商中每个出版商只要发行过一本>3的书就算)

>>> Publisher.objects.filter(book__rating__gt=3.0).annotate(num_books=Count('book'))
# 返回了至少出版了一本好书(评分大于 3 分)的出版商, 在这些出版商中只含有发行过好书的出版商!(这些出版商中每个出版商发行的所有书评分都必须>3)

order_by()

>>> Book.objects.annotate(num_authors=Count('authors')).order_by('num_authors') # 根据每本书的作者数量多少进行排序

values()

>>> Author.objects.annotate(average_rating=Avg('book__rating'))
# 返回所有作者及他所著图书的平均评分

>>> Author.objects.values('name').annotate(average_rating=Avg('book__rating'))
# 作者先按名称分组,意味着若两位作者同名则查询结果被合并!,两者均分被算为一个
>>> Author.objects.annotate(average_rating=Avg('book__rating')).values('name', 'average_rating')

# 这段代码交换了value和average顺序,将给每个作者添加一个唯一的字段,但只有作者名称和average_rating 注解会返回在输出结果中

4.查询集(QuerySet)API 查询

本质上,可以创建、过滤、切片和传递查询集而不用真实操作数据库。在你对查询集做求值之前,不会发生任何实际的数据库操作。可以通过迭代、切片、序列化/缓存、repr()、len()、list()、bool()

1)返回新的查询集方法

filter(): 返回一个新的QuerySet,包含与给定的查询参数匹配的对象。

exclude():返回一个新的QuerySet,它包含不满足给定的查找参数的对象。

annotate(*args, **kwargs): 使用提供的查询表达式Annotate查询集中的每个对象。

order_by(*fields):  默认情况下,QuerySet 根据模型Meta 类的ordering 选项排序。你可以使用order_by 方法给每个QuerySet 指定特定的排序。

...

QuerySet API参考

tricks: 利用聚合解决博客中增加点击排行和站长推荐侧边栏的方法:

views.py:

 # 点击排行
  click_list = Article.objects.all().order_by('-click_count')

 # 站长推荐
  command_list = Article.objects.filter(is_recommend__isnull=False)

base.html:

    <div  class="bd bd-news">
     <ul>
       {% for article in article_comment_list %}
       <li><a href="/" target="_blank">{{ article.title }}</a></li>
       {% endfor %}
     </ul>
   </div>

   <div class="bd bd-news">
     <ul>
       {% for article in command_list %}
       <li><a href="/" target="_blank">{{ article.title }}</a></li>
       {% endfor %}
     </ul>
    </div>

final:

本课程是一个系列的django实战进阶开发教程,目标是带领大家快速上手实战,课程以最新版本django3.2为蓝本,深入django源码本身,通过内置的类视图来开发,即CBV模式开发,从最基础的类显示视图到类编辑视图再到混入视图,由浅入深,专注Django的高级知识,带你系统的玩转Django,无过多的依赖第三方库,进一步降低学习门槛,更加专注django框架本身!本课程将带你实现一个功能完备的商城系统,如:新闻管理、商品管理(包含多规格)、订单管理、购物车、快速下单、评论、地址、运费模板等常见的商城功能!除此之外,你还可以将本课程内容中涉及到的django类视图的高级用法作为参考资料,在以后的开发中用到的时候再翻阅。认真看完这个系列视频教程之后,您会深入理解django框架的高级知识以及开发流程,具备使用django上手开发实际项目的基本能力!本课程亮点一、少量的第三方依赖,降低学习门槛开发过程中依赖第三方库少,减少大家的进一步的学习理解成本,从而更加专注django框架本身!二、前端轻度结合vue.js,无需过多前端技能本项目将轻度结合vue.js以及fetch来完成一些异步请求,无需过多vue 知识,更贴合后端开发者,也更加靠近当下最热门的技术栈,为前后端分离项目开发打下坚实的基础!三、采用django本身序列化方法构造json,深入django框架本身深度使用Django本身的序列化器来序列化数据,构造一些异步操作的接口,通过这一深度使用对未来使用drf框架将打下坚实的基础,学习drf框架将变的非常容易。四、CBV模式开发,深入Django类视图本项目全部采用Django的类视图,深入学习类视图,不同的请求我们可以在类中使用不同的方法来处理,这样大大的提高了代码的可读性以及高度扩展性,更加便于二次开发!五、多规格产品功能,更符合实际需求网上千篇一律的教程都没有深入讲解多规格商品的实现思路,本专栏将手把手带你完成多规格功能,并且对JD及TB的两种多规格模式的利弊进行深入剖析!
本门课程重实战,将基础知识拆解到项目里,让你在项目情境里学知识。 这样的学习方式能让你保持兴趣、充满动力,时刻知道学的东西能用在哪、能怎么用。 平时不明白的知识点,放在项目里去理解就恍然大悟了。   一、融汇贯通 本视频采用了前后端分离的开发模式,前端使用Vue.js+Element UI实现了Web页面的呈现,后端使用Python 的Django框架实现了数据访问的接口,前端通过Axios访问后端接口获得数据。在学习完本章节后,真正理解前后端的各自承担的工作。   二、贴近实战 本系列课程为练手项目实战:学生管理系统v4.0的开发,项目包含了如下几个内容:项目的总体介绍、基本功能的演示、Vuejs的初始化、Element UI的使用、在Django实现针对数据的增删改查的接口、在Vuejs中实现前端增删改查的调用、实现文件的上传、实现表格的分页、实现导出数据到Excel、实现通过Excel导入数据、实现针对表格的批量化操作等等,所有的功能都通过演示完成、贴近了实战   三、课程亮点 在本案例中,最大的亮点在于前后端做了分离,真正理解前后端的各自承担的工作。前端如何和后端交互   适合人群: 1、有Python语言基础、web前端基础,想要深入学习Python Web框架的朋友; 2、有Django基础,但是想学习企业级项目实战的朋友; 3、有MySQL数据库基础的朋友  
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

菲宇

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值