Menyortir item terkait dalam template Django

88

Apakah mungkin untuk mengurutkan satu set item terkait dalam template DJango?

Yaitu: kode ini (dengan tag HTML dihilangkan untuk kejelasan):

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

menampilkan hampir persis seperti yang saya inginkan. Satu-satunya hal yang ingin saya ubah adalah saya daftar peserta yang akan diurutkan berdasarkan nama belakang. Saya sudah mencoba mengatakan sesuatu seperti ini:

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

Sayangnya, sintaks di atas tidak berfungsi (menghasilkan daftar kosong) dan juga tidak ada variasi lain yang saya pikirkan (banyak kesalahan sintaks dilaporkan, tetapi tidak ada kegembiraan).

Saya dapat, tentu saja, menghasilkan semacam susunan daftar peserta yang diurutkan menurut pandangan saya, tetapi itu adalah solusi yang jelek dan rapuh (dan apakah saya menyebutkan jelek).

Tak perlu dikatakan lagi, tapi saya akan tetap mengatakannya, saya telah membaca dengan teliti dokumen online dan mencari Stack Overflow dan arsip dari django-user tanpa menemukan sesuatu yang berguna (ah, jika hanya satu set kueri adalah kamus, dictsort akan melakukan pekerjaan, tapi tidak dan tidak)

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

Diedit untuk menambahkan pemikiran tambahan setelah menerima jawaban Tawmas.


Tawmas membahas masalah ini persis seperti yang saya sampaikan - meskipun solusinya tidak seperti yang saya harapkan. Alhasil, saya mempelajari teknik berguna yang juga dapat digunakan dalam situasi lain.

Jawaban Tom mengusulkan pendekatan yang telah saya sebutkan di OP saya dan secara tentatif ditolak sebagai "jelek".

Yang "jelek" adalah reaksi naluriah, dan saya ingin mengklarifikasi apa yang salah dengannya. Dengan melakukan itu saya menyadari bahwa alasan itu adalah pendekatan yang jelek adalah karena saya terpaku pada gagasan untuk meneruskan set kueri ke templat yang akan dirender. Jika saya melonggarkan persyaratan itu, ada pendekatan tidak jelek yang seharusnya berhasil.

Saya belum mencoba ini, tapi anggaplah bahwa bukan lewat queryset, lihat kode iterasi melalui set query menghasilkan daftar Events, kemudian dihiasi setiap acara dengan satu set permintaan untuk yang sesuai peserta yang WS diurutkan (atau disaring, atau apa pun) dengan cara yang diinginkan. Sesuatu seperti itu:

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)

Sekarang templatenya menjadi:

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

Sisi negatifnya adalah tampilan harus "mengaktualisasikan" semua peristiwa sekaligus yang bisa menjadi masalah jika ada sejumlah besar peristiwa. Tentu saja seseorang bisa menambahkan pagination, tapi itu memperumit tampilan secara signifikan.

Kelebihannya adalah kode "persiapkan data untuk ditampilkan" dalam tampilan tempatnya membiarkan template fokus pada pemformatan data yang disediakan oleh tampilan untuk ditampilkan. Ini benar dan pantas.

Jadi rencana saya adalah menggunakan teknik tawmas untuk tabel besar dan teknik di atas untuk tabel kecil, dengan definisi besar dan kecil diserahkan kepada pembaca (menyeringai.)

Dale Wilson
sumber

Jawaban:

137

Anda perlu menentukan urutan dalam model peserta, seperti ini. Misalnya (dengan asumsi kelas model Anda bernama Peserta):

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

Lihat manual untuk referensi lebih lanjut.

EDIT . Solusi lain adalah menambahkan properti ke model Peristiwa Anda, yang dapat Anda akses dari template Anda:

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

Anda dapat mendefinisikan lebih banyak dari ini saat Anda membutuhkannya ...

tawma
sumber
Terima kasih atas komentar Anda, tetapi ini hanya berfungsi jika saya ingin membuat pesanan tampilan menjadi properti permanen peserta, yang tidak saya lakukan. Saya mungkin, misalnya, ingin menampilkan peserta yang diurutkan berdasarkan tanggal pendaftaran mereka diterima, jadi saya akan tahu peserta mana yang harus diberi tahu bahwa tidak ada tempat untuk mereka.
Dale Wilson
Saya menambahkan solusi alternatif untuk Anda. Ini harus memberi Anda fleksibilitas yang Anda butuhkan.
tawmas
@Mark Memang benar. Sejauh yang saya mengerti @propertyadalah berlebihan di sini karena tidak ada pengambil atau penyetel yang terlibat: stackoverflow.com/questions/1554546/…
Rick Westera
Meskipun tidak benar-benar diperlukan untuk templatnya, saya menemukan bahwa @property di sini membuat akses yang lebih bersih dari kode aplikasi , meskipun ada hukuman kinerja kecil yang terlibat (<30 ns di laptop saya). Ini tentu saja masalah gaya dan sangat subjektif.
tawmas
Jika Anda ingin mengurutkan set sehubungan dengan dua atribut, ini perintahnya: class Meta: ordering = ['last_name', 'first_name']
Tms91
140

Anda dapat menggunakan template filter dictsort https://docs.djangoproject.com/en/dev/ref/templates/builtins/#std:templatefilter-dictsort

Ini harus bekerja:

{% 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
sumber
23
Bagus ! Dan bagi mereka yang bertanya-tanya, ada juga dictsortreversed: docs.djangoproject.com/en/dev/ref/templates/builtins/…
Mickaël
1
Mengutip dari posting asli saya: ah, jika hanya satu set query adalah kamus dictsort akan melakukan pekerjaan itu, tetapi tidak dan tidak.
Dale Wilson
Saya pikir ini seharusnya lebih sedikit dalam praktiknya, dan biarkan model menangani penyortiran sebelum mengambil record.
acpmasquerade
1
@DaleWilson, saya sebenarnya dictsortbekerja dengan baik pada kode hampir persis seperti milik Anda. Menariknya tampaknya ini berfungsi dengan baik pada queryset.
mlissner
2
Dan hanya untuk kejelasan lebih lanjut, spasi penting: {% for attendee in event.attendee_set.all|dictsort:"last_name" %}mengurutkan peserta, tetapi {% for attendee in event.attendee_set.all | dictsort:"last_name" %}mencoba mengurutkan keluaran for loop dan memutus for.
mattsl
5

Salah satu solusinya adalah membuat templatag kustom:

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

gunakan seperti ini:

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

regroup seharusnya dapat melakukan apa yang Anda inginkan, tetapi adakah alasan Anda tidak dapat memesannya seperti yang Anda inginkan kembali pada tampilan?

Tom
sumber
Untuk mengurutkan mereka dalam tampilan, saya perlu mengulang melalui acara dan untuk setiap acara membuat wadah dari beberapa jenis peserta yang terkait dengan acara itu dalam urutan yang diurutkan dengan benar, lalu meneruskan seluruh koleksi wadah itu ke templat dalam bentuk yang akan membiarkan templat menemukan kumpulan peserta yang sesuai saat itu diulangi melalui acara. Seperti yang saya katakan, itu bisa dilakukan, tetapi itu bukan solusi yang baik. Ini membutuhkan terlalu banyak penggabungan antara tampilan dan template, iterasi paralel, dll. Saya berharap untuk solusi yang lebih baik untuk apa yang seharusnya menjadi masalah umum.
Dale Wilson
Saya melihat berkumpul kembali dan tampaknya tidak melakukan apa yang saya inginkan. khususnya dokumentasinya mengatakan: [quote] Perhatikan bahwa {% regroup%} tidak mengurutkan inputnya! Contoh kami mengandalkan fakta bahwa daftar orang diurutkan berdasarkan gender sejak awal. [kutipan akhir]
Dale Wilson