Як зробити SELECT MAX в Django?


91

У мене є список об’єктів, як я можу запустити запит, щоб надати максимальне значення поля:

Я використовую цей код:

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

рейтинг - це ціле число

Відповіді:


174

Дивіться це . Ваш код буде приблизно таким:

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

Ви також можете використовувати це на існуючих наборах запитів:

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

Якщо вам потрібен екземпляр моделі, який містить це максимальне значення, тоді розміщений вами код, мабуть, найкращий спосіб це зробити:

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

Зверніть увагу, що це призведе до помилки, якщо набір запитів порожній, тобто якщо жоден аргумент не відповідає запиту (оскільки [0]частина буде викликати значення IndexError). Якщо ви хочете уникнути такої поведінки і замість цього просто повернутися Noneв такому випадку, використовуйте .first():

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

4
Мені потрібен об’єкт фактичного аргументу, який має цей Макс, щоб я міг надрукувати поле деталей. Виклик args.aggregate (Max ('rating')) просто повертає найвищий рейтинг. Я шукаю аргумент з найвищим рейтингом.
Джонд,

1
Що не так із вашим вихідним кодом - Argument.objects.order_by ("- рейтинг") [0]?
AdamKG

74

Django також має функцію ' останній (ім'я_поля = Немає) ', яка знаходить останній (максимальне значення) запис. Він працює не тільки з полями дати, але також із рядками та цілими числами.

Ви можете вказати ім'я поля під час виклику цієї функції:

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

Або ви вже можете вказати це ім'я поля в метаданих своїх моделей:

from django.db import models

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

Тепер ви можете викликати 'latest ()' без будь-яких параметрів:

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

3
Це чудовий спосіб використання latest(). Якщо вам потрібен запис із мінімальним значенням, ви можете використовувати earliest().
SaeX

7
latest()і також earliest()працює з полем, що не є датою, але це побічний ефект реалізації. Вам слід використовувати <your-queryset>.order_by('<interested-field>').first()або <your-queryset>.order_by('<interested-field>').last()для того, щоб ваш код все ще працював, навіть якщо розробники Django зміняться, latest()а earliest()реалізація працюватиме лише з полями дати.
mrnfrancesco

Це погана відповідь: 1) як вже зазначалося раніше, це деталь реалізації, і її можна змінити в майбутньому 2) вона не дуже читабельна - максимальне число не обов’язково останнє збережене (або в будь-якому іншому сенсі)
Михайло Герасимов

47

Я перевірив це для свого проекту, він знаходить максимум / хв за час 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)

Це гарантовано ефективно допоможе вам отримати один із максимальних елементів, а не сортувати всю таблицю та отримувати верхню частину (близько O (n * logn)).


8
Big-O іноді не підтверджується на практиці, особливо для менших n, де коефіцієнти та імплементація мають значення. Ваш код знаходиться на python / django, який компілюється в байт-код і може бути оптимізований, а може і не оптимізований. База даних, ймовірно, написана мовою, яка оптимізована та скомпільована під машинні інструкції. Крім того, у бази даних немає реальних причин для сортування, якщо функція шукає лише максимальне значення. Я хотів би бачити дані синхронізації, перш ніж мені буде зручно використовувати код ручної збірки над вбудованою функцією бази даних.
benevolentprof

5
@afahim Я не вважаю цей код, який ви публікуєте, абсолютно правильним. якщо виявиться, що у вас більше одного максимуму (припустимо, у вас є два додатки з 5 зірками), використання "отримати" призведе до помилки.
Рейдель Міранда,
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.