Como fazer SELECT MAX no Django?

93

Eu tenho uma lista de objetos, como posso executar uma consulta para fornecer o valor máximo de um campo:

Estou 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

a classificação é um número inteiro

Johnd
fonte

Respostas:

177

Veja isso . Seu código seria algo como o seguinte:

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

Você também pode usar isso em querysets existentes:

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

Se você precisar da instância de modelo que contém esse valor máximo, o código postado é provavelmente a melhor maneira de fazer isso:

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

Observe que isso irá falhar se o queryset estiver vazio, ou seja, se nenhum argumento corresponder à consulta (porque a [0]parte levantará um IndexError). Se você quiser evitar esse comportamento e simplesmente retornar Nonenesse caso, use .first():

arg = args.order_by('-rating').first() # may return None
Sasha Chedygov
fonte
4
Preciso do objeto de argumento atual que tem esse Max, para poder imprimir o campo de detalhes. A chamada args.aggregate (Max ('rating')) retorna apenas a classificação mais alta. Estou procurando o argumento com a classificação mais alta.
Johnd
1
O que há de errado com o código de saída - Argument.objects.order_by ("- rating") [0]?
AdamKG
74

Django também tem a função ' latest (field_name = None) ' que encontra a entrada mais recente (valor máximo). Não funciona apenas com campos de data, mas também com strings e inteiros.

Você pode fornecer o nome do campo ao chamar essa função:

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

Ou você já pode dar esse nome de campo nos metadados de seus modelos:

from django.db import models

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

Agora você pode chamar 'mais recente ()' sem nenhum parâmetro:

max_rated_entry = YourModel.objects.latest()
return max_rated_entry.details
Lutz Schönemann
fonte
3
Esta é uma ótima maneira de usar latest(). Se precisar do registro com o valor mínimo, você pode usar earliest().
SaeX de
7
latest()e earliest()funciona com o campo sem data também, mas é um efeito colateral da implementação. Você deve usar <your-queryset>.order_by('<interested-field>').first()ou <your-queryset>.order_by('<interested-field>').last()para garantir que seu código ainda funcione, mesmo que os desenvolvedores Django mudem latest()e earliest()implementem para trabalhar apenas com campos de data.
mrnfrancesco 01 de
Esta é uma resposta ruim: 1) como já notado anteriormente, é um detalhe de implementação e pode ser alterado no futuro 2) não é muito legível - o número máximo não é necessariamente o último salvo (ou em qualquer outro sentido)
Mikhail Gerasimov
49

Eu testei isso para o meu projeto, ele encontra o máximo / mínimo no tempo 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)

Isso é garantido para obter um dos elementos máximos de forma eficiente, em vez de classificar a tabela inteira e obter o topo (em torno de O (n * logn)).

afahim
fonte
8
Big-O às vezes não é compatível na prática, especialmente para n menores, onde os coeficientes e a implementação são importantes. Seu código está em python / django, que é compilado para bytecode e pode ou não ser otimizado. O banco de dados provavelmente foi escrito em uma linguagem otimizada e compilada para instruções de máquina. Além disso, o banco de dados não tem nenhum motivo real para classificar, se a função estiver apenas procurando por um valor máximo. Gostaria de ver os dados de tempo antes de me sentir confortável com o uso de código de compilação manual sobre uma função de banco de dados interna.
benevolentprof de
5
@afahim Não acho que o código que você postou esteja totalmente correto. se você tiver mais de um máximo (digamos que você tenha dois aplicativos com 5 estrelas), usar "get" gerará um erro.
Raydel Miranda