521 Stimmen

Wie man als GROUP BY in django abfragen?

Ich frage ein Modell ab:

Members.objects.all()

Und sie kehrt zurück:

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

Was ich will, ist zu wissen, die beste Django Weg zu feuern a group_by Abfrage an meine Datenbank, wie:

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

Das klappt natürlich nicht. Ich weiß, wir können ein paar Tricks anwenden django/db/models/query.py aber ich bin einfach neugierig, wie man es ohne Parcheando machen kann.

778voto

Guðmundur H Punkte 10358

Wenn Sie eine Aggregation vornehmen wollen, können Sie die Aggregationsfunktionen des ORM :

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

Das Ergebnis ist eine Abfrage ähnlich wie

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

und die Ausgabe würde in der Form erfolgen

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

Wenn Sie nicht die order_by() können Sie falsche Ergebnisse erhalten, wenn die Standardsortierung nicht Ihren Erwartungen entspricht.

Wenn Sie mehrere Felder in die Ergebnisse einbeziehen möchten, fügen Sie diese einfach als Argumente zu values zum Beispiel:

    .values('designation', 'first_name', 'last_name')

Referenzen:

1 Stimmen

Du wirst nicht glauben, dass ich gerade mit demselben Stück Code jongliert habe. Ja 1.1 hat ziemlich gute Dinge zu beobachten.

3 Stimmen

Wie würden Sie einen weiteren Filter hinzufügen, um z. B. nach bestimmten Werten nach einem Datum zu suchen?

8 Stimmen

@Harry: Du kannst es verketten. Etwa so: Members.objects.filter(date=some_date).values('designation')‌​.annotate(dcount=Cou‌​nt('designation'))

78voto

Michael Punkte 770

Eine einfache Lösung, aber nicht der richtige Weg, ist die Verwendung von Roh-SQL :

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

Eine andere Lösung ist die Verwendung der group_by Eigentum:

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

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

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

1 Stimmen

Können Sie mir bitte sagen, wie ich das mit order_by? machen kann?

2 Stimmen

Hallo, wenn Sie keine Aggregation verwenden, können Sie group_by emulieren, indem Sie ein order_by verwenden und die nicht benötigten Einträge eliminieren. Natürlich ist dies eine Emulation und nur dann sinnvoll, wenn Sie nicht viele Daten verwenden. Da er nicht von Aggregation gesprochen hat, dachte ich, dass dies eine Lösung sein könnte.

0 Stimmen

Hey das ist großartig - können Sie bitte erklären, wie die Verwendung execute_sql es scheint nicht zu funktionieren.

62voto

inostia Punkte 7224

Sie können auch die regroup Template-Tag, um nach Attributen zu gruppieren. Aus den Unterlagen:

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 countries_list %}

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

Sieht so aus:

  • Indien
    • Mumbai: 19.000.000
    • Kalkutta: 15,000,000
  • USA
    • New York: 20.000.000
    • Chicago: 7.000.000
  • Japan
    • Tokio: 33,000,000

Es funktioniert auch bei QuerySet s glaube ich.

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

edit: beachten Sie die regroup Tag nicht funktioniert so, wie Sie es erwarten würden, wenn Ihre Wörterbuchliste nicht nach Schlüsseln sortiert ist. Es funktioniert iterativ. Sortieren Sie also Ihre Liste (oder Ihren Abfragesatz) nach dem Schlüssel des Groupers, bevor Sie sie an die regroup Tag.

2 Stimmen

Das ist perfekt! Ich habe lange nach einer einfachen Möglichkeit gesucht, dies zu tun. Und es funktioniert auch mit Querysets, so habe ich es verwendet.

5 Stimmen

Dies ist völlig falsch, wenn Sie aus einer Datenbank einen großen Datensatz lesen und dann nur aggregierte Werte verwenden.

1 Stimmen

@SawomirLenart Sicher, das ist vielleicht nicht so effizient wie eine direkte DB-Abfrage. Aber für einfache Anwendungsfälle kann es eine gute Lösung sein

9voto

Risadinha Punkte 14463

Das folgende Modul ermöglicht es Ihnen, Django-Modelle zu gruppieren und trotzdem mit einem QuerySet im Ergebnis zu arbeiten: 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 zum annotate / aggregate grundlegende Django-Abfragen ist die Verwendung der Attribute eines zugehörigen Feldes, z.B. book.author.last_name .

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

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

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

0 Stimmen

Diese django-group-by ist eine Alternative zum values Methode. Ich glaube, sie dient einem anderen Zweck.

1 Stimmen

@LShi Es ist keine Alternative zu Werten, natürlich nicht. values ist ein SQL select während group_by ist ein SQL group by (wie der Name schon sagt...). Warum die Herabstufung? Wir verwenden diesen Code in der Produktion zur Implementierung komplexer group_by Erklärungen.

0 Stimmen

Seine doc sagt group_by "verhält sich größtenteils wie die Werte-Methode, jedoch mit einem Unterschied..." In der Dokumentation wird SQL nicht erwähnt GROUP BY und der Anwendungsfall deutet nicht darauf hin, dass es etwas mit SQL zu tun hat GROUP BY . Ich werde die Abwärtsstimme zurückziehen, wenn jemand dies klargestellt hat, aber dieses Dokument ist wirklich irreführend.

9voto

Luis Masuelli Punkte 11452

Django unterstützt keine freien group by Abfragen . Ich habe es auf eine sehr schlechte Art und Weise gelernt. ORM ist nicht dafür ausgelegt, das zu unterstützen, was Sie tun wollen, ohne benutzerdefiniertes SQL zu verwenden. Sie sind beschränkt auf:

  • RAW sql (d. h. MyModel.objects.raw())
  • cr.execute Sätzen (und ein manuelles Parsing des Ergebnisses).
  • .annotate() (die Gruppierung nach Sätzen wird im untergeordneten Modell für .annotate() durchgeführt, in Beispielen wie der Aggregation von lines_count=Count('lines'))).

Über ein Queryset qs können Sie anrufen qs.query.group_by = ['field1', 'field2', ...] aber es ist riskant, wenn Sie nicht wissen, welche Abfrage Sie bearbeiten und keine Garantie haben, dass es funktioniert und nicht die Interna des QuerySet-Objekts beschädigt. Außerdem handelt es sich um eine interne (undokumentierte) API, auf die man nicht direkt zugreifen sollte, ohne zu riskieren, dass der Code nicht mehr mit zukünftigen Django-Versionen kompatibel ist.

3 Stimmen

In der Tat sind Sie nicht nur in der freien Gruppe-by begrenzt, so versuchen Sie SQLAlchemy anstelle von Django ORM.

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X