Django model 层之聚合查询总结

简介: Django model 层之聚合查询总结

Django model 层之聚合查询总结

 

实践环境

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/#

 

目录
相关文章
|
1月前
|
关系型数据库 MySQL Java
Django学习二:配置mysql,创建model实例,自动创建数据库表,对mysql数据库表已经创建好的进行直接操作和实验。
这篇文章是关于如何使用Django框架配置MySQL数据库,创建模型实例,并自动或手动创建数据库表,以及对这些表进行操作的详细教程。
61 0
Django学习二:配置mysql,创建model实例,自动创建数据库表,对mysql数据库表已经创建好的进行直接操作和实验。
|
1月前
|
SQL Go 数据库
【速存】深入理解Django ORM:编写高效的数据库查询
【速存】深入理解Django ORM:编写高效的数据库查询
58 0
|
1月前
|
SQL 前端开发 Python
基于python-django的neo4j人民的名义关系图谱查询系统
基于python-django的neo4j人民的名义关系图谱查询系统
32 0
|
4月前
|
SQL 数据库 Python
Django框架数据库ORM查询操作(6)
【7月更文挑战第6天】```markdown Django ORM常用数据库操作:1) 查询所有数据2) 根据ID查询 3) 精确查询 4) 分页排序
70 1
|
5月前
|
JSON 数据库 数据格式
使用 Django Q 对象构建复杂查询条件
通过本文示例,我们展示了如何使用Django的Q对象来构建复杂的查询条件,以及如何实现分页功能。Q对象的强大之处在于它能够轻松地组合多个查询条件,支持“与”、“或”关系,极大地提高了查询的灵活性和可读性。希望本文对你在实际项目中使用Django ORM构建复杂查询有所帮助。
|
5月前
|
存储 SQL 数据处理
Django ORM实战:模型字段与元选项配置,以及链式过滤与QF查询详解
Django ORM实战:模型字段与元选项配置,以及链式过滤与QF查询详解
|
5月前
|
前端开发 数据库 Python
Python Django项目下的分页和筛选查询
在Django中实现分页功能,视图函数通过`Paginator`处理数据,每页显示10条记录。URL配置支持带参数和不带参数的分页请求。前端模板使用for循环展示分页数据,包括商品信息和状态按钮,并利用分页组件导航。筛选查询视图根据GET请求的`state`参数过滤上架或下架产品,同样实现分页功能。前端添加状态选择下拉框,分页链接携带查询参数`state`确保筛选状态在翻页时保持。
|
6月前
|
SQL 缓存 数据库
在Python Web开发过程中:数据库与缓存,如何使用ORM(例如Django ORM)执行查询并优化查询性能?
在Python Web开发中,使用ORM如Django ORM能简化数据库操作。为了优化查询性能,可以:选择合适索引,避免N+1查询(利用`select_related`和`prefetch_related`),批量读取数据(`iterator()`),使用缓存,分页查询,适时使用原生SQL,优化数据库配置,定期优化数据库并监控性能。这些策略能提升响应速度和用户体验。
57 0
|
SQL JavaScript 前端开发
Django之Model世界
Model 到目前为止,当我们的程序涉及到数据库相关操作时,我们一般都会这么搞: 创建数据库,设计表结构和字段 使用MySQLdb 来连接数据库,并编写数据访问层代码 业务逻辑层去调用数据访问层执行数据库操作     import MySQLdb def GetList(sql): db = MySQLdb.
1020 0
|
15天前
|
设计模式 前端开发 数据库
Python Web开发:Django框架下的全栈开发实战
【10月更文挑战第27天】本文介绍了Django框架在Python Web开发中的应用,涵盖了Django与Flask等框架的比较、项目结构、模型、视图、模板和URL配置等内容,并展示了实际代码示例,帮助读者快速掌握Django全栈开发的核心技术。
101 44