如何在django中查询GROUP BY?

时间:2009-03-10 10:10:30

标签: python django django-models

我查询模型:

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上做一些技巧,但我很想知道如何在没有修补的情况下做到这一点。

10 个答案:

答案 0 :(得分:411)

如果您要进行汇总,可以使用aggregation features of the ORM

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

这会产生类似于

的查询
SELECT designation, COUNT(designation) AS dcount
FROM members GROUP BY designation

,输出格式为

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

答案 1 :(得分:43)

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

答案 2 :(得分:13)

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

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>

看起来像这样:

  • 印度
    • 孟买:19,000,000
    • 加尔各答:15,000,000
  • 美国
    • 纽约:20,000,000
    • 芝加哥:7,000,000
  • 日本
    • 东京:33,000,000

它也适用于QuerySet我相信。

来源:https://docs.djangoproject.com/en/2.1/ref/templates/builtins/#regroup

答案 3 :(得分:5)

您需要执行自定义SQL,如此代码段中所示:

Custom SQL via subquery

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

Adding extra Manager methods

答案 4 :(得分:4)

有一个模块允许您对Django模型进行分组,并且仍然可以在结果中使用QuerySet:https://github.com/kako-nawao/django-group-by

例如:

fftshift
fft2

&#39;书/ books.html&#39;

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) / <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查询的不同之处在于使用了相关字段的属性,例如annotate

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

aggregate

注意:book.author.last_name是Postgres特定的功能,可从Django 1.9开始提供:https://docs.djangoproject.com/en/1.10/ref/contrib/postgres/aggregates/#arrayagg

答案 5 :(得分:3)

Django不支持免费分组。我以非常糟糕的方式学到了它。 ORM不是为了支持你想要做的事情而设计的,而不使用自定义SQL。您仅限于:

  • RAW sql(即MyModel.objects.raw())
  • cr.execute句子(以及对结果的手工解析)。
  • .annotate()(句子组在.annotate()的子模型中执行,例如聚合lines_count = Count(&#39; lines&#39;)))。

在查询集qs上,您可以致电qs.query.group_by = ['field1', 'field2', ...],但如果您不知道自己正在编辑哪些查询并且无法保证其可以正常运行且不打破内部QuerySet对象。此外,它是一个内部(未记录的)API,你不应该直接访问,而不会有代码不再与未来的Django版本兼容。

答案 6 :(得分:1)

换句话说,如果您只需要根据某个字段“删除重复项”,或者只是按原样查询 ORM 对象,我想出了以下解决方法:

from django.db.models import OuterRef, Exists

qs = Members.objects.all()
qs = qs.annotate(is_duplicate=Exists(
    Members.objects.filter(
        id__lt=OuterRef('id'),
        designation=OuterRef('designation')))
qs = qs.filter(is_duplicate=False)

所以,基本上我们只是通过使用一些方便的过滤(可能会根据您的模型和要求而有所不同)来注释 is_duplicate 值,然后简单地使用该字段来过滤掉重复项。

答案 7 :(得分:0)

document表示您可以使用值对查询集进行分组。

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

您可以使用以下代码查找所有图书并按名称对其进行分组:

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

你可以看一些cheet sheet here

答案 8 :(得分:-1)

如果我没有误会你可以使用 what-query-set .group_by = [&#39; field &#39;]

答案 9 :(得分:-2)

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

首先,您需要导入Sum 然后..