Come eseguire SELECT MAX in Django?


91

Ho un elenco di oggetti come posso eseguire una query per fornire il valore massimo di un campo:

Sto usando questo codice:

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

rating è un numero intero

Risposte:


172

Vedi questo . Il tuo codice sarebbe qualcosa di simile al seguente:

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

Puoi anche usarlo su set di query esistenti:

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

Se hai bisogno dell'istanza del modello che contiene questo valore massimo, il codice che hai pubblicato è probabilmente il modo migliore per farlo:

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

Notare che questo sarà un errore se il set di query è vuoto, cioè se nessun argomento corrisponde alla query (perché la [0]parte solleverà un IndexError). Se vuoi evitare quel comportamento e invece semplicemente tornare Nonein quel caso, usa .first():

arg = args.order_by('-rating').first() # may return None

4
Ho bisogno dell'oggetto argomento effettivo che ha quel Max, quindi posso stampare il campo dei dettagli. La chiamata args.aggregate (Max ('rating')) restituisce semplicemente la valutazione più alta. Sto cercando l'arg con il punteggio più alto.
Johnd,

1
Cosa c'è di sbagliato nel tuo codice in uscita - Argument.objects.order_by ("- rating") [0]?
AdamKG,

73

Django ha anche l' ultima (field_name = None) " che trova l'ultima voce (valore massimo). Non funziona solo con i campi data ma anche con stringhe e numeri interi.

Puoi dare il nome del campo quando chiami quella funzione:

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

Oppure puoi già dare quel nome di campo nei metadati dei tuoi modelli:

from django.db import models

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

Ora puoi chiamare 'latest ()' senza alcun parametro:

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

3
Questo è un ottimo modo di usare latest(). Se hai bisogno del record con il valore minimo, puoi usare earliest().
SaeX

7
latest()e earliest()funziona anche con campi non data, ma è un effetto collaterale dell'implementazione. Dovresti usare <your-queryset>.order_by('<interested-field>').first()o <your-queryset>.order_by('<interested-field>').last()per assicurarti che il tuo codice funzioni anche se gli sviluppatori Django cambieranno latest()e l' earliest()implementazione funzionerà solo con i campi data.
mrnfrancesco

Questa è una cattiva risposta: 1) come già notato in precedenza è un dettaglio di implementazione e può essere modificato in futuro 2) non è molto leggibile - il numero massimo non è necessariamente l'ultimo salvato (o in qualsiasi altro senso)
Mikhail Gerasimov

47

L'ho testato per il mio progetto, trova il massimo / minimo in O (n) tempo:

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)

Questo è garantito per ottenere uno degli elementi massimi in modo efficiente, piuttosto che ordinare l'intera tabella e ottenere il massimo (intorno a O (n * logn)).


8
Big-O a volte non vale in pratica, specialmente per n più piccoli, dove i coefficienti e l'implementazione contano. Il tuo codice è in python / django, che è compilato in bytecode e può o non può essere ottimizzato. Il database è probabilmente scritto in un linguaggio ottimizzato e compilato in base alle istruzioni della macchina. Inoltre, il database non ha alcun motivo reale per eseguire l'ordinamento, se la funzione sta solo cercando un valore massimo. Mi piacerebbe vedere i dati sui tempi prima di essere a mio agio nell'usare il codice compilato manualmente su una funzione di database integrata.
benevolentprof

5
@afahim Non credo che questo il codice che pubblichi sia completamente corretto. se risulta che hai più di un massimo (diciamo che hai due App con 5 stelle) usando "get" si solleverà un errore.
Raydel Miranda
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.