¿Cómo hacer SELECT MAX en Django?

91

Tengo una lista de objetos, ¿cómo puedo ejecutar una consulta para dar el valor máximo de un campo?

Estoy usando este código:

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

la calificación es un número entero

Johnd
fuente

Respuestas:

172

Mira esto . Su código sería algo como lo siguiente:

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

También puede usar esto en conjuntos de consultas existentes:

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

Si necesita la instancia del modelo que contiene este valor máximo, entonces el código que publicó es probablemente la mejor manera de hacerlo:

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

Tenga en cuenta que esto generará un error si el conjunto de consultas está vacío, es decir, si ningún argumento coincide con la consulta (porque la [0]parte generará un IndexError). Si desea evitar ese comportamiento y en cambio simplemente regresar Noneen ese caso, use .first():

arg = args.order_by('-rating').first() # may return None
Sasha Chedygov
fuente
4
Necesito el objeto de argumento actual que tiene ese Max, para poder imprimir el campo de detalles. La llamada args.aggregate (Max ('rating')) solo devuelve la calificación más alta. Estoy buscando el argumento con la calificación más alta.
Johnd
1
¿Qué ocurre con el código de salida: Argument.objects.order_by ("- rating") [0]?
AdamKG
73

Django también tiene la función ' latest (field_name = None) ' que encuentra la última entrada (valor máximo). No solo funciona con campos de fecha, sino también con cadenas y números enteros.

Puede dar el nombre del campo al llamar a esa función:

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

O ya puede dar ese nombre de campo en los metadatos de sus modelos:

from django.db import models

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

Ahora puede llamar a 'latest ()' sin ningún parámetro:

max_rated_entry = YourModel.objects.latest()
return max_rated_entry.details
Lutz Schönemann
fuente
3
Esta es una excelente forma de usar latest(). Si necesita el registro con el valor mínimo, puede usar earliest().
SaeX
7
latest()y también earliest()funciona con campos sin fecha, pero es un efecto secundario de la implementación. Debes usar <your-queryset>.order_by('<interested-field>').first()o <your-queryset>.order_by('<interested-field>').last()para asegurarte de que tu código seguirá funcionando incluso si los desarrolladores de Django cambian latest()y la earliest()implementación solo funciona con campos de fecha.
mrnfrancesco
Esta es una mala respuesta: 1) como ya se notó anteriormente, es el detalle de la implementación y se puede cambiar en el futuro 2) no es muy legible: el número máximo no es necesariamente el último guardado (o en cualquier otro sentido)
Mikhail Gerasimov
47

He probado esto para mi proyecto, encuentra el máximo / mínimo en el tiempo 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)

Esto está garantizado para obtener uno de los elementos máximos de manera eficiente, en lugar de ordenar toda la tabla y obtener la parte superior (alrededor de O (n * logn)).

afahim
fuente
8
Big-O a veces no se confirma en la práctica, especialmente para n más pequeños, donde los coeficientes y la implementación son importantes. Su código está en python / django, que está compilado en código de bytes y puede o no estar optimizado. Es probable que la base de datos esté escrita en un lenguaje optimizado y compilado según las instrucciones de la máquina. Además, la base de datos no tiene una razón real para ordenar, si la función solo busca un valor máximo. Me gustaría ver los datos de tiempo antes de sentirme cómodo usando código de construcción manual sobre una función de base de datos incorporada.
benevolentprof
5
@afahim No creo que este el código que publicas sea completamente correcto. si resulta que tienes más de un máximo (digamos que tienes dos aplicaciones con 5 estrellas), usar "get" generará un error.
Raydel Miranda