Сортировка связанных элементов в шаблоне 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 %}

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

Я мог бы, конечно, создать какой-то массив отсортированных списков посетителей, на мой взгляд, но это уродливое и хрупкое (и я уже упоминал уродливое) решение.

Излишне говорить, но я все равно скажу, что я просмотрел онлайн-документацию и искал переполнение стека и архивы django-user, но не нашел ничего полезного (ах, если бы только набор запросов был словарём, dictsort сделал бы работа, но это не так и не работает)

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

Отредактировано, чтобы добавить дополнительные мысли после принятия ответа Таумаса.


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

Ответ Тома предлагал подход, который я уже упоминал в моем 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/…
Рик Вестера,
Хотя это не является строго необходимым для шаблона, я обнаружил, что здесь @property обеспечивает более чистый доступ из кода приложения , хотя есть небольшое снижение производительности (<30 нс на моем ноутбуке). Это, конечно, вопрос стиля и очень субъективный.
tawmas
Если вы хотите отсортировать набор по двум атрибутам, это команда: class Meta: ordering = ['last_name', 'first_name']
Tms91 09
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 %}
Tariwan
источник
22
Большой ! А для тех, кому интересно, есть также dictsortreversed: docs.djangoproject.com/en/dev/ref/templates/builtins/…
Mickaël
1
Цитата из моего исходного сообщения: ах, если бы только набор запросов был словарем, dictsort справился бы с этой задачей, но это не так и не работает.
Дейл Уилсон
Я думаю, что на практике этого должно быть меньше, и пусть модель обрабатывает сортировку перед извлечением записей.
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 08
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 %}
matinfo
источник
0

перегруппировка должна иметь возможность делать то, что вы хотите, но есть ли причина, по которой вы не можете упорядочить их так, как хотите, обратно в представление?

Том
источник
Чтобы упорядочить их в представлении, мне нужно будет перебирать события и для каждого события создать контейнер какого-либо типа участников, связанных с этим событием, в правильно отсортированном порядке, а затем передать всю эту коллекцию контейнеров в шаблон в форме, которая пусть бы шаблон найти подходящую коллекцию участников , как это итерации события. Как я уже сказал, это можно сделать, но это не очень хорошее решение. Это требует слишком сильной связи между представлением и шаблоном, параллельных итераций и т. Д. Я надеялся на лучшее решение того, что должно быть общей проблемой.
Дейл Уилсон,
Я посмотрел на перегруппировку, и, похоже, это не дало того, что я хотел. в частности, в документации говорится: [quote] Обратите внимание, что {% regroup%} не упорядочивает свой ввод! Наш пример основан на том факте, что список людей был упорядочен в первую очередь по полу. [endquote]
Дейл Уилсон,