Сортування пов’язаних елементів у шаблоні Django


87

Чи можна відсортувати набір пов’язаних елементів у шаблоні DJango?

Тобто: цей код (з тегами HTML для ясності опущено):

{% for event in eventsCollection %}
   {{ event.location }}
   {% for attendee in event.attendee_set.all %}
     {{ attendee.first_name }} {{ attendee.last_name }}
   {% endfor %}
 {% endfor %}

відображає майже саме те, що я хочу. Єдине, що я хочу змінити, - це список учасників, які мають бути відсортовані за прізвищем. Я намагався сказати щось подібне:

{% for event in events %}
   {{ event.location }}
   {% for attendee in event.attendee_set.order_by__last_name %}
     {{ attendee.first_name }} {{ attendee.last_name }}
   {% endfor %}
 {% endfor %}

На жаль, вищезазначений синтаксис не працює (він створює порожній список), як і будь-яка інша варіація, про яку я думав (повідомляється про багато синтаксичних помилок, але ніякої радості).

Звичайно, я міг би створити якийсь масив відсортованих списків учасників, на мій погляд, але це некрасиве та тендітне (і чи згадував я некрасиве) рішення.

Само собою зрозуміло, але я все одно скажу, що я переглянув он-лайн документи та здійснив пошук Stack Overflow та архіви django-user, не знайшовши нічого корисного (ах, якби лише набір запитів був би словниковий диктот, робота, але це не так і ні)

==================================================

Відредаговано, щоб додати додаткові думки після прийняття відповіді Таумаса.


Таумас вирішив проблему саме так, як я її представив - хоча рішення було не таким, як я очікував. В результаті я навчився корисній техніці, яку можна використовувати і в інших ситуаціях.

У відповіді Тома був запропонований підхід, про який я вже згадував у своєму OP та попередньо відхилив як "потворний".

"Потворна" була реакцією кишечника, і я хотів уточнити, що з цим не так. Роблячи це, я зрозумів, що причиною цього був потворний підхід у тому, що мене зависла ідея передати набір запитів шаблону, який буде відтворений. Якщо я зменшу цю вимогу, існує некрасивий підхід, який повинен спрацювати.

Я не пробував цього, але припущу , що замість проходження QuerySet, вид код ітерації через набір запитів виробляє список подій, потім прикрашений кожна подія з набором запитів для відповідних учасників , які БУЛИ відсортовані (або фільтрується, або що завгодно) бажаним способом. Приблизно так:

eventCollection = []   
events = Event.object.[filtered and sorted to taste]
for event in events:
   event.attendee_list = event.attendee_set.[filtered and sorted to taste]
   eventCollection.append(event)

Тепер шаблон стає:

{% for event in events %}
   {{ event.location }}
   {% for attendee in event.attendee_list %}
     {{ attendee.first_name }} {{ attendee.last_name }}
   {% endfor %}
 {% endfor %}

Недоліком є ​​те, що погляд повинен "актуалізувати" всі події одночасно, що може бути проблемою, якщо буде велика кількість подій. Звичайно, можна додати пагінацію, але це значно ускладнює погляд.

Головною стороною є код "підготувати дані до відображення" у поданні, де він належить, дозволяючи шаблону зосередитись на форматуванні даних, представлених видом для відображення. Це правильно і правильно.

Тож мій план полягає у використанні техніки Таумаса для великих столів та вищезазначеної техніки для маленьких столів, з визначенням великого та малого, що залишається читачеві (посміхається)

Відповіді:


135

Вам потрібно вказати порядок в моделі учасника, наприклад. Наприклад (якщо припустимо, що ваш клас моделі має ім'я Attendee):

class Attendee(models.Model):
    class Meta:
        ordering = ['last_name']

Див . Посібник для подальшого використання.

РЕДАГУВАТИ . Іншим рішенням є додавання властивості до вашої моделі події, до якої ви можете отримати доступ із вашого шаблону:

class Event(models.Model):
# ...
@property
def sorted_attendee_set(self):
    return self.attendee_set.order_by('last_name')

Ви можете визначити більше з них, як вони вам потрібні ...


Дякуємо за ваш коментар, але це працює, лише якщо я хочу зробити замовлення дисплея постійним властивістю учасника, чого я не роблю. Наприклад, я можу побажати показати учасників, відсортованих за датою отримання реєстрації, щоб я знав, яких учасників слід повідомити про відсутність місця для них.
Дейл Вілсон,

Я додав альтернативне рішення для вас. Це повинно забезпечити вам необхідну гнучкість.
tawmas

@Mark Дійсно. Наскільки я розумію , @propertyце надмірність тут їсти не добувачі або сетера не беруть участь: stackoverflow.com/questions/1554546 / ...
Рік Westera

Незважаючи на те, що це не є суворо необхідним для шаблону, я вважаю, що тут @property забезпечує більш чистий доступ з коду програми , хоча тут передбачено невелике покарання за продуктивність (<30 нс на моєму ноутбуці). Звичайно, це питання стилю і дуже суб’єктивне.
tawmas

Якщо ви хочете відсортувати набір за двома атрибутами, це команда: class Meta: ordering = ['last_name', 'first_name']
Tms91

135

Ви можете скористатися фільтром шаблонів dictsort https://docs.djangoproject.com/en/dev/ref/templates/builtins/#std:templatefilter-dictsort

Це має спрацювати:

{% for event in eventsCollection %}
   {{ event.location }}
   {% for attendee in event.attendee_set.all|dictsort:"last_name" %}
     {{ attendee.first_name }} {{ attendee.last_name }}
   {% endfor %}
 {% endfor %}

22
Чудово! А для тих , хто задається питанням , є також dictsortreversed: docs.djangoproject.com/en/dev/ref/templates/builtins / ...
Мікаель

1
Процитую моє оригінальне повідомлення: ах, якби лише набір запитів був словником, який би виконував диктофон, але це не так і не так.
Дейл Вілсон,

Я думаю, що на практиці цього має бути менше, і нехай модель обробляє сортування перед тим, як отримувати записи.
acpmasquerade

1
@DaleWilson, я насправді dictsortпрацюю належним чином над кодом майже так само, як ваш. Цікаво, що, здається, він чудово працює на наборах запитів.
mlissner

2
І для подальшої ясності пробіли мають значення: {% for attendee in event.attendee_set.all|dictsort:"last_name" %}сортує учасників, але {% for attendee in event.attendee_set.all | dictsort:"last_name" %}намагається сортувати вихідні дані циклу for та розриває for.
mattsl

3

Одним із рішень є створення власного шаблону:

@register.filter
def order_by(queryset, args):
    args = [x.strip() for x in args.split(',')]
    return queryset.order_by(*args)

використовувати так:

{% for image in instance.folder.files|order_by:"original_filename" %}
   ...
{% endfor %}

0

regroup повинен мати можливість робити те, що ви хочете, але чи є причина, по якій ви не можете замовити їх так, як вам хочеться, у поданні?


Щоб замовити їх у поданні, мені потрібно було б переглядати події та для кожної події створювати контейнер якихось учасників, пов’язаних із цією подією, у правильно відсортованому порядку, а потім передавати всю колекцію контейнерів шаблону у формі, яка хай би шаблон знайти підходящу колекцію учасників , як це ітерації події. Як я вже сказав, це можна зробити, але це не є гарним рішенням. Це вимагає занадто багато зв’язку між видом та шаблоном, паралельними ітераціями тощо. Я сподівався на краще рішення того, що повинно бути загальною проблемою.
Дейл Вілсон,

Я подивився на перегрупування, і, схоже, не робив того, що хотів. зокрема в документації сказано: [quote] Зверніть увагу, що {% regroup%} не впорядковує його введення! Наш приклад спирається на той факт, що список людей в першу чергу був упорядкований за статтю. [кінцева цитата]
Дейл Вілсон,
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.