• Django Aggregation聚合 django orm 求平均、去重、总和等常用方法


    Django Aggregation聚合

    在当今根据需求而不断调整而成的应用程序中,通常不仅需要能依常规的字段,如字母顺序或创建日期,来对项目进行排序,还需要按其他某种动态数据对项目进行排序。Djngo聚合就能满足这些要求。

    以下面的Model为例

     1 from django.db import models
     2  
     3 class Author(models.Model):
     4     name = models.CharField(max_length=100)
     5     age = models.IntegerField()
     6  
     7 class Publisher(models.Model):
     8     name = models.CharField(max_length=300)
     9     num_awards = models.IntegerField()
    10  
    11 class Book(models.Model):
    12     name = models.CharField(max_length=300)
    13     pages = models.IntegerField()
    14     price = models.DecimalField(max_digits=10, decimal_places=2)
    15     rating = models.FloatField()
    16     authors = models.ManyToManyField(Author)
    17     publisher = models.ForeignKey(Publisher)
    18     pubdate = models.DateField()
    19  
    20 class Store(models.Model):
    21     name = models.CharField(max_length=300)
    22     books = models.ManyToManyField(Book)
    23     registered_users = models.PositiveIntegerField()

    快速了解

     1 # books总数量.
     2 >>> Book.objects.count()
     3 2452
     4  
     5 # Total number of books with publisher=BaloneyPress
     6 >>> Book.objects.filter(publisher__name='BaloneyPress').count()
     7 73
     8  
     9 # books的平均price.
    10 >>> from django.db.models import Avg
    11 >>> Book.objects.all().aggregate(Avg('price'))
    12 {'price__avg': 34.35}
    13  
    14 # books的最大price.
    15 >>> from django.db.models import Max
    16 >>> Book.objects.all().aggregate(Max('price'))
    17 {'price__max': Decimal('81.20')}
    18  
    19 # All the following queries involve traversing the Book<->Publisher
    20 # many-to-many relationship backward
    21  
    22 # 为每个publisher添加个num_books属性,即每个pulisher出版的book的数量.
    23 >>> from django.db.models import Count
    24 >>> pubs = Publisher.objects.annotate(num_books=Count('book'))
    25 >>> pubs
    26 [<Publisher BaloneyPress>, <Publisher SalamiPress>, ...]
    27 >>> pubs[0].num_books
    28 73
    29  
    30 # 根据num_book属性排序.
    31 >>> pubs = Publisher.objects.annotate(num_books=Count('book')).order_by('-num_books')[:5]
    32 >>> pubs[0].num_books
    33 1323

    聚合生成Generating aggregates over a QuerySet

    Django有两种方法来生成聚合。第一种方法是为整个QuerySet生成聚合值,例如为全部的books生成price的平均值:

    1 >>> from django.db.models import Avg
    2 >>> Book.objects.all().aggregate(Avg('price'))
    3 {'price__avg': 34.35}

    可以简略为:

    >>> Book.objects.aggregate(Avg('price'))
    {'price__avg': 34.35}

    函数aggregate()的参数是一系列聚合函数aggregate functions:

    Avg

    返回平均值

    Count
      class Count(field, distinct=False)

    返回计数。当参数distinct=True时,返回unique的对象数目。

    Max

    返回最大值

    Min

    返回最小值.

    StdDev
      class StdDev(field, sample=False)
    返回标准偏差

    有一个参数sample

    默认情况下sample=False,返回总体标准偏差,如果sample=True,返回样本标准偏差。

    Sum

    返回总值

    Variance
      class Variance(field, sample=False)
    返回方差

    有一个参数sample,默认返回总体方差,sample设为True时返回样本方差。

    aggregate()方法被调用时,返回一个键值对字典,可以指定key的名字:

    1 >>> Book.objects.aggregate(average_price=Avg('price'))
    2 {'average_price': 34.35}

    如果你想生成多个聚合,你只需要添加另一个参数。所以,如果我们还想知道所有书的最高和最低的价格:  计算总和 平均数据 最大值 最小值方法

    1 >>> from django.db.models import Avg, Max, Min,Sum
    2 >>> Book.objects.aggregate(Avg('price'), Max('price'), Min('price'))
    3 {'price__avg': 34.35, 'price__max': Decimal('81.20'), 'price__min': Decimal('12.99')}

    为查询集的每个对象生成聚合值Generating aggregates for each item in a QuerySet

    这是生成聚合值的第二种方法。比如你要检索每本书有多少个作者。book和author是manytomany的关系,我们可以为每本书总结出这种关系。

    每个对象的总结可以用方法annotate()生成:

     1 # 建立一个annotate QuerySet
     2 >>> from django.db.models import Count
     3 >>> q = Book.objects.annotate(Count('authors'))
     4 # 第一个对象
     5 >>> q[0]
     6 <Book: The Definitive Guide to Django>
     7 >>> q[0].authors__count
     8 2
     9 # 第二个对象
    10 >>> q[1]
    11 <Book: Practical Django Projects>
    12 >>> q[1].authors__count
    13 1

    也可以指定生成属性的名字:

    >>> q = Book.objects.annotate(num_authors=Count('authors'))
    >>> q[0].num_authors
    2
    >>> q[1].num_authors
    1

    和aggregate()不同,annotate()的输出是一个QuerySet。

     联合聚合Joins and aggregates

    目前为止,我们聚合查询的field都属于我们要查询的Model,我们也可以用其它Model的field来进行聚合查询,例如:

    >>> from django.db.models import Max, Min
    >>> Store.objects.annotate(min_price=Min('books__price'), max_price=Max('books__price'))

    这样就可以查询每个Store里面books的价格范围

    联合链的深度可以随心所欲:

    1 >>> Store.objects.aggregate(youngest_age=Min('books__authors__age'))

     反向关系Following relationships backwards

    通过book反向查询publisher:

    1 >>> from django.db.models import Count, Min, Sum, Avg
    2 >>> Publisher.objects.annotate(Count('book'))

    返回的QuerySet的每个publisher都会带一个属性book_count。

    查询出版最久的书的出版日期:

    1 Publisher.objects.aggregate(oldest_pubdate=Min('book__pubdate'))

    查询每个作者写的书的总页数:

    Author.objects.annotate(total_pages=Sum('book__pages'))

    查询所有作者写的书的平均rating:

        
    >>> Author.objects.aggregate(average_rating=Avg('book__rating'))

    聚合和其它查询集操作Aggregations and other QuerySet clauses

    filter() and exclude()

    聚合可以和filter和exclude一起使用:

    1 >>> from django.db.models import Count, Avg
    2 >>> Book.objects.filter(name__startswith="Django").annotate(num_authors=Count('authors'))
    3 >>> Book.objects.filter(name__startswith="Django").aggregate(Avg('price'))

    可以根据聚合值进行筛选:

    1 >>> Book.objects.annotate(num_authors=Count('authors')).filter(num_authors__gt=1)

    编写一个包含annotate()和filter()从句的复杂查询时,要特别注意作用于QuerySet的从句的顺序顺序的不同,产生的意义也不同:

    >>> Publisher.objects.annotate(num_books=Count('book')).filter(book__rating__gt=3.0)
    >>> Publisher.objects.filter(book__rating__gt=3.0).annotate(num_books=Count('book'))

    order_by()

    可以根据聚合值进行排序:

    1 >>> Book.objects.annotate(num_authors=Count('authors')).order_by('num_authors')

    values()

    通常,注解annotate是添加到每一个对象上的,一个执行了注解操作的查询集 QuerySet 所返回的结果中,每个对象都添加了一个注解值。但是,如果使用了values()从句,它就会限制结果中列的范围,对注解赋值的方法就会完全不同。就不是在原始的 QuerySet 返回结果中对每个对象中添加注解,而是根据定义在 values() 从句中的字段组合对先结果进行唯一的分组,再根据每个分组算出注解值,这个注解值是根据分组中所有的成员计算而得的:

    2 >>> Author.objects.values('name').annotate(average_rating=Avg('book__rating'))

    这样的写法下,QuerySet会根据name进行组合,返回的是每个unique name的聚合值。如果有两个作者有相同的名字,这两个作者会被当做一个计算,他们的books会合在一起。

    1 >>>Author.objects.annotate(average_rating=Avg('book__rating')).values('name', 'average_rating')

    位置互换后,会为每个author都生成一个average_rating,而且只会输出每个author的name和average_rating。

    默认排序下使用聚合:

    from django.db import models
     
    class Item(models.Model):
        name = models.CharField(max_length=10)
        data = models.IntegerField()
     
        class Meta:
            ordering = ["name"]

    如果你想知道每个非重复的data值出现的次数,你可能这样写:

    1 # Warning: 不正确的写法
    2 Item.objects.values("data").annotate(Count("id"))

    这部分代码想通过使用它们公共的data值来分组Item对象,然后在每个分组中得到id值的总数。但是上面那样做是行不通的。这是因为默认排序项中的name也是一个分组项,所以这个查询会根据非重复的(data,name)进行分组,而这并不是你本来想要的结果。所以,你需要这样写来去除默认排序的影响:

    1 Item.objects.values("data").annotate(Count("id")).order_by()

    Aggregating annotations

    也可以根据annotation结果生成聚合值,例如计算每本书平均有几个作者:

    1 >>> from django.db.models import Count, Avg
    2 >>> Book.objects.annotate(num_authors=Count('authors')).aggregate(Avg('num_authors'))
    3 {'num_authors__avg': 1.66}
  • 相关阅读:
    elementui表单输入框部分校验--判断
    vue学习如何引入js,封装操作localStorage本地储存的方法
    vue组件传值 递增次数传递bug修复
    vue深度监听之手机格式344
    vue-------滑动验证
    vue get/post请求如何携带cookie的问题
    elementui的dialog组件踩坑
    WebStorm License Activation (WebStorm许可证激活)
    input 标签实现带提示文字的输入框
    隐藏 input 标签的边框
  • 原文地址:https://www.cnblogs.com/zhaoyingjie/p/9243564.html
Copyright © 2020-2023  润新知