首页 文章

如何在django中查询GROUP BY?

提问于
浏览
249

我查询一个模型,

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

9 回答

  • 39

    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 .

  • 3

    Django does not support free group by queries . 我以非常糟糕的方式学到了它 . ORM不是为了支持你想要做的事情而设计的,而不使用自定义SQL . 您仅限于:

    • RAW sql(即MyModel.objects.raw())

    • cr.execute 句子(以及对结果的手工解析) .

    • .annotate() (按照句子分组在.annotate()的子模型中执行,例如聚合lines_count = Count('lines'))) .

    在查询集 qs 上,您可以调用 qs.query.group_by = ['field1', 'field2', ...] ,但如果您不知道您正在编辑哪个查询并且无法保证它将起作用并且不会破坏QuerySet对象的内部,则存在风险 . 此外,它是一个内部(未记录的)API,你不应该直接访问,而不会有代码不再与未来的Django版本兼容 .

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

    首先你需要导入Sum然后..

  • -1

    您还可以使用 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 s我相信 .

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

  • 5

    有一个模块允许您对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>
    

    annotate / aggregate 基本Django查询的不同之处在于使用相关字段的属性,例如: book.author.last_name .

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

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

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

  • 0

    一个简单的解决方案,但不是以正确的方式使用RAW-SQL:

    http://docs.djangoproject.com/en/dev/topics/db/sql/#topics-db-sql

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

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

    您现在可以迭代结果变量以检索结果 . 请注意,group_by没有记录,可能会在将来的Django版本中更改 .

    而且......你为什么要使用group_by?如果不使用聚合,则可以使用order_by来获得相似的结果 .

  • 4

    如果我没有误会你可以使用,what-query-set.group_by = ['field']

  • 377

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

    Custom SQL via subquery

    或者在在线Django文档中显示的自定义管理器中:

    Adding extra Manager methods

  • -3

    如果您要进行聚合,可以使用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}]
    

相关问题