Comment faire SELECT MAX dans Django?

91

J'ai une liste d'objets comment puis-je exécuter une requête pour donner la valeur maximale d'un champ:

J'utilise ce code:

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

la note est un entier

Johnd
la source

Réponses:

172

Regarde ça . Votre code ressemblerait à ceci:

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

Vous pouvez également l'utiliser sur des ensembles de requêtes existants:

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

Si vous avez besoin de l'instance de modèle qui contient cette valeur maximale, le code que vous avez publié est probablement la meilleure façon de le faire:

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

Notez que cela entraînera une erreur si l'ensemble de requêtes est vide, c'est-à-dire si aucun argument ne correspond à la requête (car la [0]partie lèvera un IndexError). Si vous voulez éviter ce comportement et simplement revenir Nonedans ce cas, utilisez .first():

arg = args.order_by('-rating').first() # may return None
Sasha Chedygov
la source
4
J'ai besoin de l'objet argument actuall qui a ce Max, donc je peux imprimer le champ de détails. L'appel args.aggregate (Max ('rating')) renvoie simplement la note la plus élevée. Je recherche l'argument avec la note la plus élevée.
Johnd
1
Quel est le problème avec votre code de sortie - Argument.objects.order_by ("- rating") [0]?
AdamKG
73

Django a également la fonction ' latest (field_name = None) ' qui trouve la dernière entrée (valeur max.). Cela fonctionne non seulement avec les champs de date, mais également avec des chaînes et des entiers.

Vous pouvez donner le nom du champ lors de l'appel de cette fonction:

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

Ou vous pouvez déjà donner ce nom de champ dans les métadonnées de vos modèles:

from django.db import models

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

Vous pouvez maintenant appeler 'latest ()' sans aucun paramètre:

max_rated_entry = YourModel.objects.latest()
return max_rated_entry.details
Lutz Schönemann
la source
3
C'est une excellente façon d'utiliser latest(). Si vous avez besoin de l'enregistrement avec la valeur minimale, vous pouvez utiliser earliest().
SaeX
7
latest()et earliest()fonctionne également avec un champ non-date, mais c'est un effet secondaire de l'implémentation. Vous devez utiliser <your-queryset>.order_by('<interested-field>').first()ou <your-queryset>.order_by('<interested-field>').last()pour vous assurer que votre code fonctionnera toujours même si les développeurs Django vont changer latest()et earliest()implémenter pour travailler avec les champs de date uniquement.
mrnfrancesco
C'est une mauvaise réponse: 1) comme déjà remarqué plus tôt, c'est le détail de la mise en œuvre et peut être changé à l'avenir 2) il n'est pas très lisible - le nombre maximum n'est pas nécessairement le dernier enregistré (ou dans un autre sens)
Mikhail Gerasimov
47

J'ai testé cela pour mon projet, il trouve le max / min en temps 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)

Ceci est garanti pour vous obtenir l'un des éléments maximum efficacement, plutôt que de trier toute la table et d'obtenir le haut (autour de O (n * logn)).

Afahim
la source
8
Big-O n'est parfois pas valable en pratique, en particulier pour les n plus petits, où les coefficients et l'implémentation sont importants. Votre code est en python / django, qui est compilé en bytecode et peut ou non être optimisé. La base de données est probablement écrite dans un langage optimisé et compilé en instructions machine. De plus, la base de données n'a aucune vraie raison de trier, si la fonction ne recherche qu'une valeur maximale. J'aimerais voir les données de synchronisation avant de pouvoir utiliser du code de construction manuelle sur une fonction de base de données intégrée.
benevolentprof
5
@afahim Je ne pense pas que le code que vous publiez soit complètement correct. s'il s'avère que vous avez plus d'un maximum (disons que vous avez deux applications avec 5 étoiles) l'utilisation de "get" soulèvera une erreur.
Raydel Miranda