关于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}]

  • 你不会相信我现在在处理同一段代码。是的,1.1有很多好东西要看。
  • 如何添加另一个过滤器,让我们假设按日期查找不同的值?
  • @哈利:你可以把它拴起来。比如:Members.objects.filter(date=some_date).values('designation')‌​.annotate(dcount=Cou‌​nt('designation'))
  • 我有一个问题,这个查询只返回指定和数据计数,如果我也想得到表的其他值呢?
  • 请注意,如果排序不是指定字段,则在不重置排序的情况下,它将无法工作。见stackoverflow.com/a/1341667/202137
  • @gidgidonihah是的,例子应该是Members.objects.order_by('disignation').values('designation'‌​).annotate(dcount=Co‌​unt('designation'))
  • 我可以在没有count的情况下执行查询吗?
  • 我有一个问题,这个查询只返回指定和数据计数,如果我也想得到表的其他值呢?
  • @Clayton:将其他字段放在值括号内,如下所示:members.objects.values('designation'、'field2'、'field3').annotate(dcount=count('designation'))
  • @yann leave:将其他字段放在值括号内,如下所示:members.objects.values('designation'、'field2'、'field3')。annotate(dcount=count('designation'))
  • 有时我这样注释,它相当于从members group by members.id中选择designation、count(designation)作为dcount。为什么会这样?
  • 这必须(尽管是可选的)以.order_by(designation)结束,原因是取决于模型或数据库,它们将是一个隐式order_by到位,完全破坏了分组机制。所以在这里明确的保存。
  • @moutio如果我将另一个字段放入值中,那么它将按规则分组,例如:values("designation","field2"),然后它将按field2而不是designation分组。有没有其他方法可以实现这样的查询?从表group by field2中选择max(field1)、field2、field3。


一个简单的解决方案,但不是正确的方法是使用原始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来获得相同的结果。

  • 你能告诉我怎么用订货方式吗??
  • 嗨,如果您不使用聚合,您可以通过使用order by模拟group_,并消除不需要的条目。当然,这是一个仿真,仅在使用的数据不多时才可用。因为他没有提到聚合,我认为这可能是一个解决方案。
  • 嘿,这很好-你能解释一下如何使用执行SQL吗?它似乎不起作用。
  • 注意,这不再适用于Django 1.9。stackoverflow.com/questions/35558120/…
  • 它似乎在Django 1.11上有效。好的特点。
  • 这是一种使用ORM的黑客方式。您不必手动实例化传入旧查询集的新查询集。


还可以使用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/重新组合

  • 这太完美了!我一直在寻找一种简单的方法。它也适用于查询集,这就是我使用它的方式。
  • 如果您从数据库中读取大量数据,然后只使用聚合值,那么这是完全错误的。
  • @?当然,这可能不如直接的数据库查询有效。但对于简单的用例来说,这可能是一个很好的解决方案。
  • 如果结果显示在模板中,这将起作用。但是,对于jsonResponse或其他间接响应。这个解决方案不起作用。
  • @如果您想在视图中执行此操作,例如stackoverflow.com/questions/477820/&hellip;可能对您有用


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

通过子查询自定义SQL

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

添加额外的管理器方法

  • 一种往返的解决方案。如果我能继续使用的话,我会用它的。但在这里,我只需要每个职位的成员数量就够了。
  • 没问题。我考虑过提到1.1聚合特性,但假设您使用的是发布版本:)
  • 这一切都是关于使用原始查询,这显示了Django的ORM的弱点。


有一个模块允许您对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 Group By是values方法的替代方法。我想这是为了不同的目的。
  • @当然,它不是价值观的替代品。values是sql selectgroup_by是sql group by(顾名思义…)。为什么投反对票?我们在生产中使用这种代码来实现复杂的group_by语句。
  • 它的文档说group_by的行为与values方法基本相同,但有一点不同……文档没有提到sql GROUP BY,它提供的用例也没有暗示它与sql GROUP BY有任何关系。当有人说清楚了这一点时,我会撤回投票,但那份文件确实是误导性的。
  • 在阅读了values的文档后,我发现我错过了values本身的工作方式就像一个分组方式。这是我的错。我认为在values不足的情况下,使用itertools.groupby比使用django组简单。
  • 用一个简单的values调用(带或不带annotate)和不从数据库中获取所有内容,从上面执行GROUP BY是不可能的。您对itertools.groupby的建议适用于小型数据集,但不适用于您可能希望翻页的数千个数据集。当然,在这一点上,无论如何,您必须考虑包含准备好的(已经分组的)数据的特殊搜索索引。
  • values的Doc与我在Django1.8的经历有点不同。(也许版本在这里很重要,或者我还没有理解。)这个问题演示了一个典型的用法,它需要一个order_by(如果没有这个用法,结果将需要进一步处理)。
  • 抱歉,我之前的评论误导了我"从上面"写的内容。因此,我在自己的答案中引用了一个例子,我提供了这个例子来帮助其他人处理更复杂的用例。


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

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

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

  • 实际上,您不仅限于自由分组,所以请尝试使用sqlacalchemy而不是django orm。


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

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']

  • 事实并非如此,至少在Django 1.6中:"queryset"对象没有"groupu by"属性。
  • 正确的用法可以是queryset.query.group_by=[…],但这会破坏查询的语义,并且不能按预期工作。


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

首先您需要导入SUM然后…