Jak zrobić SELECT MAX w Django?

91

Mam listę obiektów, w jaki sposób mogę uruchomić zapytanie, aby podać maksymalną wartość pola:

Używam tego kodu:

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

ocena jest liczbą całkowitą

Johnd
źródło

Odpowiedzi:

172

Zobacz to . Twój kod wyglądałby mniej więcej tak:

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

Możesz również użyć tego w istniejących zestawach zapytań:

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

Jeśli potrzebujesz instancji modelu, która zawiera tę maksymalną wartość, prawdopodobnie najlepszym sposobem na to jest opublikowany kod:

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

Zauważ, że spowoduje to błąd, jeśli zestaw zapytań jest pusty, tj. Jeśli żaden argument nie pasuje do zapytania (ponieważ [0]część podniesie IndexError). Jeśli chcesz uniknąć tego zachowania i zamiast tego po prostu wrócić Nonew takim przypadku, użyj .first():

arg = args.order_by('-rating').first() # may return None
Sasha Chedygov
źródło
4
Potrzebuję obiektu rzeczywistego argumentu, który ma tę wartość Max, więc mogę wydrukować pole szczegółów. Wywołanie args.aggregate (Max ('rating')) zwraca właśnie najwyższą ocenę. Szukam argumentu o najwyższej ocenie.
Johnd
1
Co jest nie tak z twoim końcowym kodem - Argument.objects.order_by ("- rating") [0]?
AdamKG
73

Django ma również funkcję „ latest (field_name = None) ”, która wyszukuje najnowszy (maks. Wartość) wpis. Działa nie tylko z polami daty, ale także z łańcuchami i liczbami całkowitymi.

Podczas wywoływania tej funkcji możesz podać nazwę pola:

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

Lub możesz już podać nazwę tego pola w metadanych swoich modeli:

from django.db import models

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

Teraz możesz wywołać „najnowsze ()” bez żadnych parametrów:

max_rated_entry = YourModel.objects.latest()
return max_rated_entry.details
Lutz Schönemann
źródło
3
To świetny sposób korzystania latest(). Jeśli potrzebujesz rekordu z minimalną wartością, możesz użyć earliest().
SaeX
7
latest()i earliest()działa również z polem innym niż data, ale jest to efekt uboczny implementacji. Powinieneś użyć <your-queryset>.order_by('<interested-field>').first()lub, <your-queryset>.order_by('<interested-field>').last()aby upewnić się, że twój kod będzie nadal działał, nawet jeśli programiści Django zmienią się latest()i earliest()zaimplementują, aby działały tylko z polami daty.
mrnfrancesco
To zła odpowiedź: 1) jak już wcześniej zauważyłem, jest to szczegół implementacji i może zostać zmieniony w przyszłości 2) nie jest zbyt czytelny - maksymalna liczba niekoniecznie jest ostatnią zapisaną (lub w jakimkolwiek innym sensie)
Michaił Gierasimow
47

Przetestowałem to dla mojego projektu, znajduje maksymalny / min w czasie O (n):

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)

Gwarantuje to wydajne uzyskanie jednego z maksymalnych elementów, zamiast sortowania całej tabeli i uzyskiwania szczytu (wokół O (n * logn)).

afahim
źródło
8
Big-O czasami nie sprawdza się w praktyce, szczególnie dla mniejszego n, gdzie współczynniki i implementacja mają znaczenie. Twój kod jest w python / django, który jest kompilowany do kodu bajtowego i może być zoptymalizowany lub nie. Baza danych jest prawdopodobnie napisana w języku zoptymalizowanym i skompilowanym do instrukcji maszynowych. Poza tym baza danych nie ma prawdziwego powodu do sortowania, jeśli funkcja szuka tylko wartości maksymalnej. Chciałbym zobaczyć dane dotyczące czasu, zanim zacznę używać kodu tworzonego ręcznie na podstawie wbudowanej funkcji bazy danych.
benevolentprof
5
@afahim Nie sądzę, aby ten kod, który publikujesz, był całkowicie poprawny. jeśli okaże się, że masz więcej niż jedno maksimum (powiedzmy, że masz dwie aplikacje z 5 gwiazdkami), użycie polecenia „pobierz” spowoduje zgłoszenie błędu.
Raydel Miranda,