Wie mache ich SELECT MAX in Django?

91

Ich habe eine Liste von Objekten. Wie kann ich eine Abfrage ausführen, um den Maximalwert eines Felds anzugeben:

Ich benutze diesen Code:

def get_best_argument(self):
    try:
        arg = self.argument_set.order_by('-rating')[0].details
    except IndexError:
        return 'no posts'
    return arg

Bewertung ist eine ganze Zahl

Johnd
quelle

Antworten:

170

Sehen Sie das . Ihr Code würde ungefähr so ​​aussehen:

from django.db.models import Max
# Generates a "SELECT MAX..." query
Argument.objects.aggregate(Max('rating')) # {'rating__max': 5}

Sie können dies auch für vorhandene Abfragesätze verwenden:

from django.db.models import Max
args = Argument.objects.filter(name='foo') # or whatever arbitrary queryset
args.aggregate(Max('rating')) # {'rating__max': 5}

Wenn Sie die Modellinstanz benötigen, die diesen Maximalwert enthält, ist der von Ihnen veröffentlichte Code wahrscheinlich der beste Weg, dies zu tun:

arg = args.order_by('-rating')[0]

Beachten Sie, dass dies fehlschlägt, wenn das Abfrageset leer ist, dh wenn keine Argumente mit der Abfrage übereinstimmen (da das [0]Teil ein löst IndexError). Wenn Sie dieses Verhalten vermeiden und stattdessen einfach Nonein diesem Fall zurückkehren möchten , verwenden Sie .first():

arg = args.order_by('-rating').first() # may return None
Sasha Chedygov
quelle
4
Ich benötige das tatsächliche Argumentobjekt mit diesem Max, damit ich das Detailfeld drucken kann. Der Aufruf von args.aggregate (Max ('Bewertung')) gibt nur die höchste Bewertung zurück. Ich suche das Argument mit der höchsten Bewertung.
Johnd
1
Was ist falsch an Ihrem Code - Argument.objects.order_by ("- Bewertung") [0]?
AdamKG
72

Django hat auch die Funktion ' latest (field_name = None) ', die den neuesten (max. Wert) Eintrag findet. Es funktioniert nicht nur mit Datumsfeldern, sondern auch mit Zeichenfolgen und Ganzzahlen.

Sie können den Feldnamen angeben, wenn Sie diese Funktion aufrufen:

max_rated_entry = YourModel.objects.latest('rating')
return max_rated_entry.details

Oder Sie können diesen Feldnamen bereits in den Metadaten Ihrer Modelle angeben:

from django.db import models

class YourModel(models.Model):
    #your class definition
    class Meta:
        get_latest_by = 'rating'

Jetzt können Sie 'latest ()' ohne Parameter aufrufen:

max_rated_entry = YourModel.objects.latest()
return max_rated_entry.details
Lutz Schönemann
quelle
3
Dies ist eine großartige Möglichkeit zur Verwendung latest(). Wenn Sie den Datensatz mit dem Mindestwert benötigen, können Sie ihn verwenden earliest().
SaeX
7
latest()und earliest()funktioniert auch mit Nicht-Datumsfeldern, aber es ist ein Nebeneffekt der Implementierung. Sie sollten verwenden <your-queryset>.order_by('<interested-field>').first()oder <your-queryset>.order_by('<interested-field>').last()sicherstellen, dass Ihr Code auch dann noch funktioniert, wenn Django-Entwickler Änderungen latest()und earliest()Implementierungen vornehmen, um nur mit Datumsfeldern zu arbeiten.
mrnfrancesco
Dies ist eine schlechte Antwort: 1) Wie bereits früher bemerkt, ist es ein Detail der Implementierung und kann in Zukunft geändert werden. 2) Es ist nicht sehr lesbar. Die maximale Anzahl ist nicht unbedingt die zuletzt gespeicherte (oder in einem anderen Sinne)
Mikhail Gerasimov,
46

Ich habe dies für mein Projekt getestet, es findet die maximale / min in O (n) Zeit:

from django.db.models import Max

# Find the maximum value of the rating and then get the record with that rating. 
# Notice the double underscores in rating__max
max_rating = App.objects.aggregate(Max('rating'))['rating__max']
return App.objects.get(rating=max_rating)

Auf diese Weise erhalten Sie garantiert eines der maximalen Elemente, anstatt die gesamte Tabelle zu sortieren und die Spitze zu erreichen (um O (n * logn)).

afahim
quelle
8
Big-O ist in der Praxis manchmal nicht sinnvoll, insbesondere bei kleineren n, bei denen Koeffizienten und Implementierung eine Rolle spielen. Ihr Code befindet sich in Python / Django, das zu Bytecode kompiliert wurde und möglicherweise optimiert ist oder nicht. Die Datenbank ist wahrscheinlich in einer Sprache geschrieben, die optimiert und nach Maschinenanweisungen kompiliert wurde. Außerdem hat die Datenbank keinen wirklichen Grund zum Sortieren, wenn die Funktion nur nach einem Maximalwert sucht. Ich möchte Zeitdaten sehen, bevor ich handgefertigten Code über eine integrierte Datenbankfunktion verwenden kann.
Wohlwollender
5
@afahim Ich denke nicht, dass der Code, den Sie posten, völlig korrekt ist. Wenn sich herausstellt, dass Sie mehr als ein Maximum haben (sagen wir, Sie haben zwei Apps mit 5 Sternen), wird die Verwendung von "get" einen Fehler auslösen.
Raydel Miranda