Хорошие способы отсортировать набор запросов? - Джанго

112

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

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

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


Какие-либо предложения?

RadiantHex
источник
4
@RH: так как насчет проверки ответа AlexMartelli как правильного решения? (не то чтобы ему больше не нужна была репутация, если только он не собирается
преследовать

Ответы:

191

Что о

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 (* поля)

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

Пример:

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

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

Алекс Мартелли
источник
1
@Alex: grazie alex! Это было здорово, я иду читать о модуле оператора!
RadiantHex 09
3
Это более эффективно, чем Author.objects.order_by ('- score', 'last_name') [: 30]?
Брайан Люфт
4
@Brian - если под «более эффективным» вы имеете в виду «более правильный», то да, это так. :) В вашем решении авторы в значительной степени отсортированы по количеству баллов, и в алфавитном порядке располагаются только авторы с одинаковым баллом (так работают вторичные ключи). Алекс показывает, как получить результаты, а затем применить к ним совершенно другой порядок сортировки (используя key = operator.attrgetter для определения выражения ключа для каждого объекта), что и просил OP.
PaulMcG
@ Пол: я не это спрашивал, ответ Алекса правильный!
RadiantHex
4
Почему бы не сделать ключ сортировки функцией lambda x: x.last_name? Он короче, многословен и не требует импорта.
Krzysztof Szularz
12

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

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)

Используя решение Алекса Мартелли, которое точно определяет 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)

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

jathanism
источник
13
Ваш результат №3 сортируется по убыванию по количеству очков, а затем по last_name IFF все объекты имеют одинаковое количество очков. Проблема в том, что ни один из объектов в вашем наборе результатов не имеет одинаковой оценки, поэтому только «-счет» влияет на порядок сортировки. Попробуйте установить для трех авторов оценку 487 и снова запустить №3.
istruble
Да, я это понимаю. Я действительно просто хотел проиллюстрировать, что встроенные решения (только SQL) не всегда самые лучшие.
jathanism
3
Он сделал именно то, что я ожидал: лексикографическое упорядочение (что довольно тривиально, если все первые ключи сортировки различны).
Йонас Кёлькер,
5

Вот способ, который учитывает ничью по отсеченному счету.

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')

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

istruble
источник