Хороші способи сортування набору запитів? - Джанго


111

що я намагаюся зробити, це це:

  • отримати 30 авторів з найбільшою оцінкою ( Author.objects.order_by('-score')[:30])

  • упорядкувати авторів за last_name


Будь-які пропозиції?


4
@RH: то як щодо перевірки відповіді AlexMartelli як правильного рішення? (не те, що йому потрібно більше представника, якщо тільки він не піде за цим хлопцем зі Скіта ...)
PaulMcG

Відповіді:


190

А як на рахунок

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опцією в мета Meta. Ви можете змінити це за принципом QuerySet, використовуючи order_byметод.

Приклад:

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

Отриманий вище результат буде упорядкований у порядку scoreзменшення, потім у last_nameпорядку зростання. Негативний знак "-score"вказує на порядок зменшення. Порядок зростання - мається на увазі.


1
@ Алекс: grazie alex! Це було чудово, я збираюся прочитати про модуль оператора!
RadiantHex

3
Це більш ефективно, ніж Author.objects.order_by ('- оцінка', 'прізвище') [: 30]?
Брайан Люфт

4
@Brian - якщо під "ефективнішим" ви маєте на увазі "правильніше", то так, його. :) Ваше рішення тримає авторів в основному відсортованих за балами, і лише алфавітує тих авторів з однаковою оцінкою (саме так працюють вторинні ключі). Алекс показує, як приймати результати, а потім застосовувати до них зовсім інший порядок сортування (використовуючи key = operator.attrgetter для визначення ключового вираження для кожного об'єкта), про що вимагає ОП.
PaulMcG

@Paul: це не те, що я запитав, але відповідь Алекса є правильною!
RadiantHex

4
Чому б не зробити функцію ключа сортування lambda x: x.last_name? Він коротший, більш багатослівний і не потребує жодного імпорту.
Кшиштоф Шулаж

12

Я просто хотів проілюструвати, що вбудовані рішення (лише для SQL) не завжди є найкращими. Спочатку я подумав, що оскільки 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)

Використовуючи рішення Алекса Мартеллі, яке точно забезпечує топ-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)

Як бачите, це такий же результат, як і перший, тобто він не працює так, як ви очікували.


13
Ваш результат №3 сортує за спаданням за балом, а потім за прізвищем IFF будь-які об’єкти мають однаковий бал. Проблема полягає в тому, що жоден з об'єктів у вашому наборі результатів не має однакових балів, тому лише порядок "-оцінок" впливає на порядок сортування. Спробуйте встановити 3 бали авторів до 487 і запустити №3 знову.
istruble

Так, я це розумію. Мені дуже хотілося проілюструвати, що вбудовані рішення (лише для SQL) не завжди є найкращими.
ятанізм

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

Ви можете отримати більше 30 авторів у top_authors таким чином, і min(30,author_count)якщо у вас менше 30 авторів.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.