Django model 层之聚合查询总结

by:授客 QQ1033553122

实践环境

Python版本:python-3.4.0.amd64

下载地址:https://www.python.org/downloads/release/python-340/

Win7 64位

 

Django  1.11.4

下载地址:https://www.djangoproject.com/download/

聚合查询

MySQL数据库为例,假设项目目录结构如下:

mysite/

myapp/

__init__.py

admin.py

apps.py

migrations/

__init__.py

models.py

tests.py

views.py

    manage.py
    mysite/
        __init__.py
        settings.py
        urls.py
        wsgi.py

models.py内容如下:

from django.db import models

# Create your models
here.

class
Person(models.Model):

first_name = models.CharField(max_length=30)

last_name = models.CharField(max_length=30)

class Book(models.Model):

book_name = models.CharField(max_length=30)

borrower = models.ForeignKey(Person,
to_field='id', on_delete=models.CASCADE)

class
Store(models.Model):

id = models.AutoField(primary_key=True)

name = models.CharField(max_length=50)

last_update =
models.DateField(auto_now=True)

class
Production_addr(models.Model):

addr = models.CharField(max_length=50)

distance = models.IntegerField()

class
Fruit(models.Model):

store = models.ManyToManyField(Store)

production_addr =
models.ForeignKey(Production_addr, to_field='id', on_delete=models.CASCADE)

name = models.CharField(max_length=100)

onsale_date = models.DateField()

price = models.IntegerField()

class
Taste(models.Model):

taste = models.CharField(max_length=50)

fruit=models.ManyToManyField(Fruit)

class
News(models.Model):

title = models.CharField(max_length=20)

n_comments = models.IntegerField()

n_pingbacks = models.IntegerField()

rank = models.IntegerField()

class Blog(Book):

author = models.CharField(max_length=50)

针对整个QuerySet生成聚合

例:查询myapp_news表中,rank值大于26的记录,其n_comments平均值

>>> from myapp.models import News

>>> from django.db.models
import Avg

>>>
News.objects.filter(rank__gt=26).aggregate(Avg('n_comments'))

{'n_comments__avg': 17.0}

如果是针对所有记录求均值,我们可以这样

>>>
News.objects.all().aggregate(Avg('n_comments'))

{'n_comments__avg': 23.0}

也可以去掉all()

>>>
News.objects.aggregate(Avg('n_comments'))

{'n_comments__avg': 23.0}

返回结果说明
{'聚合结果值标识':'聚合结果值'}

自定义聚合结果标识

>>> News.objects.all().aggregate(average_price
= Avg('n_comments'))

{'average_price': 23.0}

>>> from django.db.models
import Avg, Max, Min

>>>
News.objects.aggregate(Avg('n_comments'), Max('n_comments'), Min('n_comments'))

{'n_comments__max': 35, 'n_comments__min': 14,
'n_comments__avg': 23.0}

针对整个QuerySet的每项生成聚合

可以理解为mysql中的分组统计,Model.objects.annotate(……) ,不过不一样的是,这里没有指定分组字段,是按每个model对象分组。

例子:Fruit和Store model存在多对多关系。现在需要查询,myapp_fruit表中某条记录(可以理解为每类水果),有多少家商店在出(myapp_store表中每条记录对应一个商店)

>>> from myapp.models import Fruit,
Production_addr, Store, Taste

>>> from django.db.models
import Count

>>> q =
Fruit.objects.annotate(Count('store'))

>>> q[0]

<Fruit: Fruit object>

>>> q[0].store__count

1

>>> q[1].store__count

3

>>> q[2].store__count

1

默认的,annotation标识由aggregate函数及被聚合field而来(例中为store__count),类似aggregate, 可以自定义annotation标识

>>> q =
Fruit.objects.annotate(store_num = Count('store'))

>>> q[0].store_num

1

>>>

和aggregate不同的是,annotate()语句输出结果为QuerySet,支持其它QuerySet操作,包括filter(),order_by(),甚至是再次调用annotate()

>>> q =
Fruit.objects.annotate(store_num = Count('store')).filter(id__gt=3)

>>> q[0]

<Fruit: Fruit object>

>>> q[0].store_num

3

混用多个聚合函数

使用annotate()函数,混用多个聚合函数,会返回错误的结果,因为实现使用的是join查询,而非子查询。针对count聚合函数,可以使用distinct=True参数避免这个问题

例子:检索myapp_fruit表中第一个条记录,查询出售该类水果的商店数及该类水果的口味总数。

>>> fruit = Fruit.objects.first()

>>> fruit.store.count()

2

>>> fruit.taste_set.count()

3

>>> from django.db.models import Count

>>> q = Fruit.objects.annotate(Count('store'),
Count('taste'))

>>> q[0].store__count

6

>>> q[0].taste__count

6

解决方法:

>>> q = Fruit.objects.annotate(Count('store',
distinct=True), Count('taste', distinct=True))

>>> q[0].taste__count

3

>>> q[0].store__count

2

>>>

联合查询与聚合

有时候,需要获取和当前正在查询模块关联的另一个模块的相关聚合值,这个时候,可在聚合函数中,指定字段使用双下划线方式,关联相关模块进行join查询

例子:检索myapp_store表,查询每个商店正在出售水果种类中,最高价和最低价。

>>> q = Store.objects.annotate(min_price=Min('fruit__price'),
max_price=Max('fruit__price'))

>>> for item in q:

...    
print(item.min_price, item.max_price)

...

10 20

19 20

None None

None None

None None

None None

联合查询的深度取决于你的查询要求。

例子:检索myapp_store表,查询每个商店正在出售水果种类中,产地最远是多远。

>>> from django.db.models import Min, Max

>>> q = Store.objects.annotate(max_distance=

Min('fruit__production_addr__distance'))

>>> for item in q:

...    
print(item.name, item.max_distance)

...

aimi 40

ximi 20

xima None

masu None

gami None

gama None

反向关联查询

例:查询每个产地的水果种类数量(myapp_production_addr.id是myapp_fruit表的外键)

>>> q =
Production_addr.objects.annotate(cnt=Count('fruit'))

>>> for item in q:

...    
print(item.addr, item.cnt)

...

changting 1

shanghang 1

longyan 1

例,检索所有产地产出的水果种类,最小价格

>>> q =
Production_addr.objects.aggregate(min_price=Min('fruit__price'))

>>> print(q)

{'min_price': 10}

对比(分组统计):

>>> q =
Production_addr.objects.annotate(min_price=Min('fruit__price'))

>>> for item in q:

...    
print(item.addr, item.min_price)

...

changting 20

shanghang 16

longyan 10

不仅仅是针对外键,针对多对多关系也可以

>>> from django.db.models
import Avg

>>> q =
Taste.objects.annotate(avg_price=Avg('fruit__price'))

>>> for item in q:

...    
print(item.taste, item.avg_price)

...

sour 20.0

sweet 20.0

bitter 20.0

聚合及其它QuerySet语句

filter()exclude()

例子:统计myapp_fruit表中banana除外的水果种类的最小价

>>>
Fruit.objects.exclude(name='banana').aggregate(Min('price'))

{'price__min': 16}

filter也支持类似用法

Filtering on annotations

例子:检索myapp_store表,查询每个商店正在出售水果种类中最低价,过滤最低价小于等于10的。

>>>
Store.objects.annotate(min_price=Min('fruit__price')).filter(min_price__gt=10)

说明:先执行annotate,得到结果集,然后执行filter语句,得出结果。

注意:annotationsfilter()exclude()语句是有先后顺序之分的,后一步的处理依赖前一步的结果,顺序不一样,结果可能也会也不一样。

 

order_by()

例子:检索myapp_store表,查询每个商店正在出售水果种类中最低价,并按最低价升许排序。

>>>
Store.objects.annotate(min_price=Min('fruit__price')).order_by('min_price')

<QuerySet [<Store: Store object>, <Store:
Store object>, <Store: Store object>,<Store: Store object>,
<Store: Store object>, <Store: Store object>]>

values()

values()结合annotate的使用

例子:检索myapp_store表,按商店名称分组查询商店正在出售水果种类中最低价

>>>
Store.objects.values('name').annotate(min_price=Min('fruit__price'))

<QuerySet [{'min_price': 10, 'name': 'aimi'},
{'min_price': 19, 'name': 'ximi'}, {'min_price': None, 'name': 'xima'},
{'min_price': None, 'name': 'masu'}, {'min_price': None, 'name': 'gami'},
{'min_price': None, 'name': 'gama'}]>

>>>

可以理解为mysql中的分组统计,values('filed')中指定filed即为分组统计字段

注意:类似filter()valuesannotate也有先后顺序之分。

 

annotateaggregate配合使用

例:

>>> Store.objects.values('name').annotate(min_price=Min('fruit__price')).aggregate(Avg('min_price'))

{'min_price__avg': 14.5}

 

说明,链式处理

其它例子

参考链接:https://www.cnblogs.com/YingLai/p/6601243.html

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

v
= models.UserInfo.objects.values('u_id').annotate(uid=Count('u_id'))

#
SELECT u_id, COUNT(ui) AS `uid` FROM UserInfo GROUP BY u_id

v=
models.UserInfo.objects.values('u_id').annotate(uid=Count('u_id')).filter(uid__gt=1)

#
SELECT u_id, COUNT(ui_id) AS `uid` FROM UserInfo GROUP BY u_id having
count(u_id) > 1

v
=
models.UserInfo.objects.values('u_id').annotate(uid=Count('u_id',distinct=True)).filter(uid__gt=1)

#
SELECT u_id, COUNT( DISTINCT ui_id) AS `uid` FROM UserInfo GROUP BY u_id having
count(u_id) > 1

更多详情,参考链接:

https://docs.djangoproject.com/en/1.11/topics/db/aggregation/#

Django model 层之聚合查询总结的更多相关文章

  1. django第10天(聚合查询,常用字段)

    django第10天 聚合查询 聚合函数的使用场景 单独使用:不分组,只查聚合结果 分组使用:按字段分组,可查分组字段与聚合结果 导入聚合函数 from django.db.models import ...

  2. Django 学习 之ORM聚合查询分组查询与F查询与Q查询

    一.聚合查询和分组查询 1.聚合查询aggregate 关于数据表的数据请见上一篇:Django 学习 之ORM多表操作(点我) aggregate(*args, **kwargs),只对一个组进行聚 ...

  3. Django模型层之字段查询参数及聚合函数

    该系列教程系个人原创,并完整发布在个人官网刘江的博客和教程 所有转载本文者,需在顶部显著位置注明原作者及www.liujiangblog.com官网地址. 字段查询是指如何指定SQL WHERE子句的 ...

  4. Django框架:8、聚合查询、分组查询、F与Q查询、ORM查询优化、ORM事务操作、ORM常用字段类型、ORM常用字段参数

    Django 数据库 目录 Django 数据库 一.聚合查询 二.分组查询 三.F查询与Q查询 1.F查询 2.Q查询 3.Q查询进阶操作 四.ORM查询优化 1.only与defer 五.ORM事 ...

  5. {django模型层(二)多表操作}一 创建模型 二 添加表记录 三 基于对象的跨表查询 四 基于双下划线的跨表查询 五 聚合查询、分组查询、F查询和Q查询

    Django基础五之django模型层(二)多表操作 本节目录 一 创建模型 二 添加表记录 三 基于对象的跨表查询 四 基于双下划线的跨表查询 五 聚合查询.分组查询.F查询和Q查询 六 xxx 七 ...

  6. Django框架第七篇(模型层)--多表操作:一对多/多对多增删改,跨表查询(基于对象、基于双下划线跨表查询),聚合查询,分组查询,F查询与Q查询

    一.多表操作 一对多字段的增删改(book表和publish表是一对多关系,publish_id字段) 增  create publish_id 传数字   (publish_id是数据库显示的字段名 ...

  7. django系列5.5--分组查询,聚合查询,F查询,Q查询,脚本中调用django环境

    一.聚合查询 aggregate(*args, **args) 先引入需要的包,再使用聚合查询 #计算所有图书的平均价格 from django.db.models import Avg Book.o ...

  8. python 之 Django框架(orm单表查询、orm多表查询、聚合查询、分组查询、F查询、 Q查询、事务、Django ORM执行原生SQL)

    12.329 orm单表查询 import os if __name__ == '__main__': # 指定当前py脚本需要加载的Django项目配置信息 os.environ.setdefaul ...

  9. django聚合查询

    聚合¶ Django 数据库抽象API 描述了使用Django 查询来增删查改单个对象的方法.然而,有时候你需要获取的值需要根据一组对象聚合后才能得到.这份指南描述通过Django 查询来生成和返回聚 ...

  10. day056-58 django多表增加和查询基于对象和基于双下划线的多表查询聚合 分组查询 自定义标签过滤器 外部调用django环境 事务和锁

    一.多表的创建 from django.db import models # Create your models here. class Author(models.Model): id = mod ...

随机推荐

  1. edge ctrl+c 复制不起作用

    现象: 在edge中选中文字时,按一次ctrl+c,到其它程序ctrl+v,并不是你选中的内容. 需要在edge中按2次ctrl+c才行. --- 当你选中文字时,会出现一个三个点的菜单,点击后会出现 ...

  2. spring项目中读取resources下的文件

    spring项目中读取resources下的文件 我们都知道,spring项目经过maven打包后,可以打成jar包或可解压的war包 a. war包是需要外置的web容器去运行的,是需要先解压的 b ...

  3. 盘点 Spring Boot 解决跨域请求的几种办法

    熟悉 web 系统开发的同学,对下面这样的错误应该不会太陌生. 之所以会出现这个错误,是因为浏览器出于安全的考虑,采用同源策略的控制,防止当前站点恶意攻击 web 服务器盗取数据. 01.什么是跨域请 ...

  4. 新浪微博动态 RSA 分析图文+登录

    Tips:当你看到这个提示的时候,说明当前的文章是由原emlog博客系统搬迁至此的,文章发布时间已过于久远,编排和内容不一定完整,还请谅解` 新浪微博动态 RSA 分析图文+登录 日期:2016-10 ...

  5. 在Winform程序中动态绘制系统名称,代替图片硬编码名称

    在以前我做程序的时候,一般在登录窗口里面显示程序名称,登录窗口一般设置一张背景图片,由于程序的名称一般都是确定的,所以也不存在太大的问题,不过如果客户定制不同的系统的时候,需要使用Photoshop修 ...

  6. 支撑阻力指标,庄家成本价是可靠的支撑位(无未来,DLL加密)

    本指标依据庄家的成本价设计的,庄家成本价是可靠的支撑位.底层逻辑:庄家是有内幕的, 庄家能在价格低位时抄底,庄家控股时,庄家不会让散户获取低价的筹码,所以当股价到达到支撑位时,会有比较大的反弹.庄家也 ...

  7. pytest执行_allure报“AttributeError: module 'allure' has no attribute 'severity_level'”

    背景: 一个大项目A,需要项目B作为源码,即pycharm的 source_root 问题: 项目B,执行pytest.main([pytest命令]),控制台报错"AttributeErr ...

  8. CentOS8 安装ansible

    # 安装epel扩展源 yum install https://dl.fedoraproject.org/pub/epel/epel-release-latest-8.noarch.rpm -y # ...

  9. 玉炜的iOS开发规范(12.14更新)

    苹果账号篇 1.新建一个新项目之后的bundle ID无论是否定没定,上架的账号如果没定,最好先写一个绝对用不上的名字,因为这个bundle ID一经确定会被绑定在当前你的苹果账号上,如果正好绑定的是 ...

  10. Gerrit 大量代码提交流程优化

    # Gerrit 大量代码提交流程优化 背景 本文适用于 提交大量初始代码 的场景(仓库版本的早期). 有时候提交大量代码到Gerrit的时候会提示: $ git push Counting obje ...