web-dev-qa-db-de.com

Wie als GROUP BY in Django abfragen?

Ich frage ein Modell ab, 

Members.objects.all()

und es kehrt zurück

Eric, Salesman, X-Shop
Freddie, Manager, X2-Shop
Teddy, Salesman, X2-Shop
Sean, Manager, X2-Shop

Was ich will, ist, die beste Django-Methode zu kennen, um eine Gruppe_abfrage an meine Datenbank abzufeuern.

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

Was nicht funktioniert. Ich weiß, wir können ein paar Tricks mit "Django/db/models/query.py", Machen, aber ich bin nur neugierig, wie man das ohne Patching macht.

256
simplyharsh

Wenn Sie Aggregation durchführen möchten, können Sie die Aggregationsfunktionen des ORM verwenden:

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

Dies führt zu einer Abfrage ähnlich wie

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

und die Ausgabe wäre von der Form

[{'designation': 'Salesman', 'dcount': 2}, 
 {'designation': 'Manager', 'dcount': 2}]
394
Guðmundur H

Eine einfache Lösung, aber nicht in geeigneter Weise, ist die Verwendung von RAW-SQL:

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

Eine andere Lösung ist die Verwendung der group_by -Eigenschaft:

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

Sie können jetzt die Ergebnisvariable iterieren, um Ihre Ergebnisse abzurufen. Beachten Sie, dass group_by nicht dokumentiert ist und in zukünftigen Versionen von Django geändert werden kann.

Und ... warum möchtest du group_by verwenden? Wenn Sie keine Aggregation verwenden, können Sie order_by verwenden, um ein ähnliches Ergebnis zu erzielen.

40
Michael

Sie können auch das Vorlagen-Tag regroup verwenden, um nach Attributen zu gruppieren. Aus den Dokumenten:

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>

Sieht aus wie das:

  • Indien
    • Mumbai: 19.000.000
    • Kalkutta: 15.000.000 
  • VEREINIGTE STAATEN VON AMERIKA
    • New York: 20.000.000
    • Chicago: 7.000.000
  • Japan
    • Tokio: 33.000.000

Es funktioniert auch auf QuerySets, glaube ich.

source: https://docs.djangoproject.com/de/1.11/ref/templates/builtins/#regroup

10
inostia

Sie müssen benutzerdefiniertes SQL wie in diesem Snippet veranschaulicht ausführen:

Benutzerdefinierte SQL über Unterabfrage

Oder in einem benutzerdefinierten Manager, wie in den Online-Django-Dokumenten gezeigt:

Zusätzliche Manager-Methoden hinzufügen

5
Van Gale

Es gibt ein Modul, mit dem Sie Django-Modelle gruppieren können und dennoch mit einem QuerySet im Ergebnis arbeiten können: https://github.com/kako-nawao/Django-group-by

Zum Beispiel:

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)

'book/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>

Der Unterschied zu den annotate/aggregate Basis-Django-Abfragen besteht in der Verwendung der Attribute eines verwandten Feldes, z. book.author.last_name.

Wenn Sie die PKs der zusammengruppierten Instanzen benötigen, fügen Sie die folgende Anmerkung hinzu:

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

HINWEIS: ArrayAgg ist eine Postgres-spezifische Funktion, verfügbar ab Django 1.9: https://docs.djangoproject.com/de/1.10/ref/contrib/postgres/aggregates/#arrayagg

4
Risadinha

Django unterstützt keine freie Gruppe durch Abfragen . Ich habe es auf sehr schlechte Weise gelernt. ORM unterstützt nicht die Dinge, die Sie möchten, ohne benutzerdefinierte SQL zu verwenden. Sie sind beschränkt auf:

  • RAW-SQL (d. H. MyModel.objects.raw ())
  • cr.execute Sätze (und eine handgefertigte Analyse des Ergebnisses).
  • .annotate() (die Gruppierung nach Sätzen wird im Kindermodell für .annotate () ausgeführt, in Beispielen wie z. B. Aggregation von lines_count = Count ('lines'))).

Über ein Abfrageset qs können Sie qs.query.group_by = ['field1', 'field2', ...] aufrufen. Es ist jedoch riskant, wenn Sie nicht wissen, welche Abfrage Sie gerade bearbeiten, und es keine Garantie dafür gibt, dass sie funktioniert und nicht das Intern des QuerySet-Objekts beschädigt. Außerdem handelt es sich dabei um eine interne (undokumentierte) API, auf die Sie nicht direkt zugreifen sollten, ohne zu riskieren, dass der Code nicht mehr mit zukünftigen Django-Versionen kompatibel ist.

3
Luis Masuelli

Das document besagt, dass Sie Werte verwenden können, um das Abfrageset zu gruppieren. 

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

Mit diesem Code können Sie alle Bücher finden und nach Namen gruppieren:

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

Sie können sich ein Cheet Sheet hier anschauen.

0
ramwin