我查询一个模型:
Members.objects.all()
它返回:
Eric, Salesman, X-Shop
Freddie, Manager, X2-Shop
Teddy, Salesman, X2-Shop
Sean, Manager, X2-Shop
我想要的是知道Django最好的点火方式
对我的数据库进行group_by查询,如:
Members.objects.all().group_by('designation')
当然,这是行不通的。
我知道我们可以在django/db/models/query.py上做一些技巧,但我只是好奇如何不打补丁就能做到。
这有点复杂,但让提问者他/她期望只有一个DB命中。
from django.db.models import Subquery, OuterRef
member_qs = Members.objects.filter(
pk__in = Members.objects.values('designation').distinct().annotate(
pk = Subquery(
Members.objects.filter(
designation= OuterRef("designation")
)
.order_by("pk") # you can set other column, e.g. -pk, create_date...
.values("pk")[:1]
)
)
.values_list("pk", flat=True)
)
如果你想进行聚合,你可以使用ORM的聚合特性:
from django.db.models import Count
result = (Members.objects
.values('designation')
.annotate(dcount=Count('designation'))
.order_by()
)
这将导致类似于
SELECT designation, COUNT(designation) AS dcount
FROM members GROUP BY designation
输出就是这样的形式
[{'designation': 'Salesman', 'dcount': 2},
{'designation': 'Manager', 'dcount': 2}]
如果不包括order_by(),如果默认排序不是您所期望的,则可能会得到不正确的结果。
如果你想在结果中包含多个字段,只需将它们作为参数添加到值中,例如:
.values('designation', 'first_name', 'last_name')
引用:
Django文档:values(), annotation()和Count
Django文档:聚合,特别是标题为“与默认排序或order_by()的交互”的部分。
一个简单的解决方案,但不是正确的方法是使用原始SQL:
results = Members.objects.raw('SELECT * FROM myapp_members GROUP BY designation')
另一个解决方案是使用group_by属性:
query = Members.objects.all().query
query.group_by = ['designation']
results = QuerySet(query=query, model=Members)
现在可以遍历结果变量以检索结果。请注意,group_by没有文档,在Django的未来版本中可能会被更改。
和…为什么要使用group_by?如果不使用聚合,可以使用order_by来实现类似的结果。
下面的模块允许你对Django模型进行分组,并且仍然使用结果中的QuerySet: https://github.com/kako-nawao/django-group-by
例如:
from django_group_by import GroupByMixin
class BookQuerySet(QuerySet, GroupByMixin):
pass
class Book(Model):
title = TextField(...)
author = ForeignKey(User, ...)
shop = ForeignKey(Shop, ...)
price = DecimalField(...)
class GroupedBookListView(PaginationMixin, ListView):
template_name = 'book/books.html'
model = Book
paginate_by = 100
def get_queryset(self):
return Book.objects.group_by('title', 'author').annotate(
shop_count=Count('shop'), price_avg=Avg('price')).order_by(
'name', 'author').distinct()
def get_context_data(self, **kwargs):
return super().get_context_data(total_count=self.get_queryset().count(), **kwargs)
“本书/ books.html”
<ul>
{% for book in object_list %}
<li>
<h2>{{ book.title }}</td>
<p>{{ book.author.last_name }}, {{ book.author.first_name }}</p>
<p>{{ book.shop_count }}</p>
<p>{{ book.price_avg }}</p>
</li>
{% endfor %}
</ul>
与注释/聚合基本Django查询的区别在于使用了相关字段的属性,例如book.author.last_name。
如果您需要已经分组在一起的实例的pk,请添加以下注释:
.annotate(pks=ArrayAgg('id'))
注意:ArrayAgg是Postgres特定的函数,从Django 1.9开始可用:https://docs.djangoproject.com/en/3.2/ref/contrib/postgres/aggregates/#arrayagg