Хорошие способы сортировки набора запросов? - Django

Я пытаюсь сделать следующее:

  • получите 30 авторов с наивысшим результатом (Author.objects.order_by('-score')[:30])

  • упорядочить авторов last_name


Любые предложения?

Ответ 1

Что насчет

import operator

auths = Author.objects.order_by('-score')[:30]
ordered = sorted(auths, key=operator.attrgetter('last_name'))

В Django 1.4 и новее вы можете заказать, предоставив несколько полей.
Ссылка: https://docs.djangoproject.com/en/dev/ref/models/querysets/#order-by

order_by (* поля)

По умолчанию результаты, возвращаемые QuerySet, упорядочиваются упорядочивающим кортежем, заданным параметром ordering в моделях Meta. Вы можете переопределить это для каждого запроса на основе QuerySet, используя метод order_by.

Пример:

ordered_authors = Author.objects.order_by('-score', 'last_name')[:30]

Приведенный выше результат будет упорядочен по score по убыванию, затем по last_name по возрастанию. Отрицательный знак перед "-score" указывает убывающий порядок. Подразумевается возрастающий порядок.

Ответ 2

Я просто хотел проиллюстрировать, что встроенные решения (только для SQL) не всегда являются лучшими. Сначала я подумал, что, поскольку метод Django QuerySet.objects.order_by принимает несколько аргументов, вы можете легко связать их:

ordered_authors = Author.objects.order_by('-score', 'last_name')[:30]

Но это не работает так, как вы ожидали. В качестве примера можно привести список президентов, отсортированных по счету (выбор 5-го уровня для упрощения чтения):

>>> auths = Author.objects.order_by('-score')[:5]
>>> for x in auths: print x
... 
James Monroe (487)
Ulysses Simpson (474)
Harry Truman (471)
Benjamin Harrison (467)
Gerald Rudolph (464)

Используя решение Alex Martelli, которое точно отображает 5 лучших людей, отсортированных по last_name:

>>> for x in sorted(auths, key=operator.attrgetter('last_name')): print x
... 
Benjamin Harrison (467)
James Monroe (487)
Gerald Rudolph (464)
Ulysses Simpson (474)
Harry Truman (471)

И теперь комбинированный вызов order_by:

>>> myauths = Author.objects.order_by('-score', 'last_name')[:5]
>>> for x in myauths: print x
... 
James Monroe (487)
Ulysses Simpson (474)
Harry Truman (471)
Benjamin Harrison (467)
Gerald Rudolph (464)

Как вы можете видеть, это тот же результат, что и первый, что означает, что он не работает так, как вы ожидали.

Ответ 3

Вот способ, который позволяет связывать для отметки отсечения.

author_count = Author.objects.count()
cut_off_score = Author.objects.order_by('-score').values_list('score')[min(30, author_count)]
top_authors = Author.objects.filter(score__gte=cut_off_score).order_by('last_name')

Вы можете получить более 30 авторов в top_authors таким образом, а min(30,author_count) - это то, что у вас меньше 30 авторов.