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.

8voto

ralfzen Punkte 215

Sie können auch die integrierten Pythons verwenden itertools.groupby direkt:

from itertools import groupby

designation_key_func = lambda member: member.designation
queryset = Members.objects.all().select_related("designation")

for designation, member_group in groupby(queryset, designation_key_func):
    print(f"{designation} : {list(member_group)}")

Kein raw sql, subqueries, third-party-libs oder templatetags benötigt und pythonic und explizit in meinen Augen.

12 Stimmen

Wie sieht es mit der Leistung aus?

7voto

ramwin Punkte 4882

En Dokumentation besagt, dass Sie Werte verwenden können, um die Abfragegruppe 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 sie nach Namen gruppieren:

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

Sie können sich einen Spickzettel ansehen aquí .

0 Stimmen

Warum brauchen Sie group_by(), um das richtige Ergebnis zu erhalten?

4voto

Van Gale Punkte 42727

Sie müssen benutzerdefiniertes SQL verwenden, wie in diesem Ausschnitt gezeigt wird:

Benutzerdefiniertes SQL über Unterabfrage

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

Hinzufügen zusätzlicher Manager-Methoden

1 Stimmen

Eine Art Hin- und Rücklösung. Ich hätte es benutzt, wenn ich eine längere Verwendung dafür hätte. Aber hier brauche ich nur die Anzahl der Mitglieder pro Bezeichnung, das ist alles.

0 Stimmen

Das ist kein Problem. Ich dachte daran, die Aggregationsfunktionen von 1.1 zu erwähnen, ging aber davon aus, dass Sie die Release-Version verwenden :)

0 Stimmen

Es geht um die Verwendung von Rohabfragen, die die Schwäche von Djangos ORM zeigen.

2voto

rumbarum Punkte 432

Dies ist etwas kompliziert, aber der Fragesteller erhält mit nur einem DB-Treffer das, was er erwartet.

from django.db.models import Subquery, OuterRef

member_qs = Members.objects.filter(
    pk__in = Members.objects.values('designation').distinct().annotate(
        pk = Subquery(
          Members.objects.filter(
            designation= OuterRef("designation")
        )
        .order_by("pk") # you can set other column, e.g. -pk, create_date...
        .values("pk")[:1]
        ) 
    )
   .values_list("pk", flat=True)
)

1voto

Raekkeri Punkte 751

Wenn Sie mit anderen Worten nur "Duplikate entfernen" müssen, die auf einem Feld basieren, und ansonsten nur die ORM-Objekte abfragen wollen, wie sie sind, habe ich folgende Lösung gefunden:

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)

Im Grunde kommentieren wir also nur die is_duplicate Wert, indem Sie eine praktische Filterung verwenden (die je nach Modell und Anforderungen variieren kann), und dann einfach dieses Feld verwenden, um die Duplikate herauszufiltern.

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