关于python:如何在django中查询为GROUP BY?

How to query as GROUP BY in django?

我有个模特

1
Members.objects.all()

And it returns:

ZZU1

我想要知道最好的火焰之路查询我的数据库,比如:

1
Members.objects.all().group_by('designation')

不工作的,不工作的我知道我们可以在EDOCX1上做一些作弊,但我很好奇,如果不加标记,我们怎么办?


如果要进行聚合,可以使用ORM的聚合功能:

1
2
from django.db.models import Count
Members.objects.values('designation').annotate(dcount=Count('designation'))

这将导致类似于

1
2
SELECT designation, COUNT(designation) AS dcount
FROM members GROUP BY designation

输出的形式是

1
2
[{'designation': 'Salesman', 'dcount': 2},
 {'designation': 'Manager', 'dcount': 2}]


一个简单的解决方案,但不是正确的方法是使用原始SQL:

1
results = Members.objects.raw('SELECT * FROM myapp_members GROUP BY designation')

另一种解决方案是使用group_by属性:

1
2
3
query = Members.objects.all().query
query.group_by = ['designation']
results = QuerySet(query=query, model=Members)

现在可以迭代results变量来检索结果。请注意,group_by没有记录在案,可能在Django的未来版本中更改。

还有…为什么要使用group_by?如果不使用聚合,则可以使用order_by来获得相同的结果。


还可以使用regroup模板标记按属性分组。来自文档:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
cities = [
    {'name': 'Mumbai', 'population': '19,000,000', 'country': 'India'},
    {'name': 'Calcutta', 'population': '15,000,000', 'country': 'India'},
    {'name': 'New York', 'population': '20,000,000', 'country': 'USA'},
    {'name': 'Chicago', 'population': '7,000,000', 'country': 'USA'},
    {'name': 'Tokyo', 'population': '33,000,000', 'country': 'Japan'},
]

...

{% regroup cities by country as country_list %}


<ul>

    {% for country in country_list %}
       
<li>
{{ country.grouper }}
           
<ul>

            {% for city in country.list %}
               
<li>
{{ city.name }}: {{ city.population }}
</li>

            {% endfor %}
           
</ul>

       
</li>

    {% endfor %}

</ul>

如下所示:

  • 印度
    • 孟买:19000000
    • 加尔各答:15000000
  • 美国
    • 纽约:20000000
    • 芝加哥:70万
  • 日本
    • 东京:33000000

我相信它也适用于QuerySet的系统。

来源:https://docs.djangoproject.com/en/2.1/ref/templates/builtins/重新组合


您需要按照以下代码段中的示例执行自定义SQL:

通过子查询自定义SQL

或者在自定义管理器中,如在线django文档所示:

添加额外的管理器方法


有一个模块允许您对django模型进行分组,并在结果中使用查询集:https://github.com/kako-nawao/django-group-by

例如:

1
2
3
4
5
6
7
8
9
10
from django_group_by import GroupByMixin

class BookQuerySet(QuerySet, GroupByMixin):
    pass

class Book(Model):
    title = TextField(...)
    author = ForeignKey(User, ...)
    shop = ForeignKey(Shop, ...)
    price = DecimalField(...)
1
2
3
4
5
6
7
8
9
10
11
12
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'

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<ul>

{% for book in object_list %}
   
<li>

        {{ 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>

annotate/aggregate基本django查询的区别在于使用相关字段的属性,例如book.author.last_name

如果需要组合在一起的实例的pk,请添加以下注释:

1
.annotate(pks=ArrayAgg('id'))

注:ArrayAgg是postgres特有的功能,从django 1.9起提供:https://docs.djangoproject.com/en/1.10/ref/contrib/postgres/aggregates/arrayagg


Django不支持自由分组查询。我是以非常糟糕的方式学会的。ORM的设计不支持您想做的事情,而不使用自定义SQL。您仅限于:

  • 原始SQL(即myModel.objects.raw())
  • cr.execute句(以及对结果的手工分析)。
  • .annotate()(按语句分组在.annotate()的子模型中执行,例如聚合行数=count(‘行’)。

在查询集qs上,您可以调用qs.query.group_by = ['field1', 'field2', ...],但如果您不知道正在编辑的查询是什么,并且不能保证它可以工作并且不会破坏queryset对象的内部,则会有风险。此外,它是一个内部(未记录的)API,您不应该在不冒代码不再与未来的Django版本兼容的风险的情况下直接访问它。


文档说明可以使用值对查询集进行分组。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
class Travel(models.Model):
    interest = models.ForeignKey(Interest)
    user = models.ForeignKey(User)
    time = models.DateTimeField(auto_now_add=True)

# Find the travel and group by the interest:

>>> Travel.objects.values('interest').annotate(Count('user'))
<QuerySet [{'interest': 5, 'user__count': 2}, {'interest': 6, 'user__count': 1}]>
# the interest(id=5) had been visited for 2 times,
# and the interest(id=6) had only been visited for 1 time.

>>> Travel.objects.values('interest').annotate(Count('user', distinct=True))
<QuerySet [{'interest': 5, 'user__count': 1}, {'interest': 6, 'user__count': 1}]>
# the interest(id=5) had been visited by only one person (but this person had
#  visited the interest for 2 times

您可以找到所有书籍,并使用以下代码按名称分组:

1
Book.objects.values('name').annotate(Count('id')).order_by() # ensure you add the order_by()

你可以在这里看一些厚棉布。


如果我没有误解,您可以使用任何查询集。group_by=['field']


1
2
from django.db.models import Sum
Members.objects.annotate(total=Sum(designation))

首先您需要导入SUM然后…