Sortieren verwandter Elemente in einer Django-Vorlage

86

Ist es möglich, eine Reihe verwandter Elemente in einer DJango-Vorlage zu sortieren?

Das heißt: dieser Code (wobei HTML-Tags aus Gründen der Übersichtlichkeit weggelassen wurden):

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

Displays wollen fast genau ich will. Das einzige, was ich ändern möchte, ist die Liste der Teilnehmer, die nach Nachnamen sortiert werden sollen. Ich habe versucht, so etwas zu sagen:

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

Leider funktioniert die obige Syntax nicht (sie erzeugt eine leere Liste) und auch keine andere Variation, an die ich gedacht habe (viele Syntaxfehler wurden gemeldet, aber keine Freude).

Ich könnte meiner Ansicht nach natürlich eine Reihe sortierter Teilnehmerlisten erstellen, aber das ist eine hässliche und fragile (und habe ich hässliche erwähnt) Lösung.

Unnötig zu sagen, aber ich werde es trotzdem sagen, ich habe die Online-Dokumente durchgesehen und Stack Overflow und die Archive von Django-Benutzern durchsucht, ohne etwas Hilfreiches zu finden (ah, wenn nur ein Abfragesatz ein Wörterbuch wäre, würde Dictsort das tun Job, aber es ist nicht und es nicht)

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

Bearbeitet, um zusätzliche Gedanken hinzuzufügen, nachdem Tawmas 'Antwort akzeptiert wurde.


Tawmas ging das Problem genau so an, wie ich es vorgestellt hatte - obwohl die Lösung nicht meinen Erwartungen entsprach. Als Ergebnis habe ich eine nützliche Technik gelernt, die auch in anderen Situationen angewendet werden kann.

Toms Antwort schlug einen Ansatz vor, den ich bereits in meinem OP erwähnt und vorläufig als "hässlich" abgelehnt hatte.

Das "Hässliche" war eine Bauchreaktion, und ich wollte klarstellen, was daran falsch war. Dabei wurde mir klar, dass der Grund dafür ein hässlicher Ansatz war, weil ich auf die Idee gekommen war, einen Abfragesatz an die zu rendernde Vorlage zu übergeben. Wenn ich diese Anforderung lockere, gibt es einen hässlichen Ansatz, der funktionieren sollte.

Ich habe das noch nicht ausprobiert, aber annehmen , dass anstatt die queryset vorbei, die View - Code durch den Abfragesatz iteriert, eine Liste der Ereignisse produzieren dann jedes Ereignis mit einer Abfrage für die entsprechenden Teilnehmer eingerichtet , die WAR sortiert (oder gefiltert, oder was auch immer) auf die gewünschte Weise. So etwas in der Art:

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)

Jetzt wird die Vorlage:

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

Der Nachteil ist, dass die Ansicht alle Ereignisse auf einmal "aktualisieren" muss, was ein Problem sein könnte, wenn es eine große Anzahl von Ereignissen gibt. Natürlich könnte man Paginierung hinzufügen, aber das erschwert die Ansicht erheblich.

Der Vorteil ist, dass sich der Code "Vorbereiten der anzuzeigenden Daten" in der Ansicht befindet, in die er gehört, sodass sich die Vorlage auf die Formatierung der von der Ansicht zur Anzeige bereitgestellten Daten konzentrieren kann. Das ist richtig und richtig.

Mein Plan ist es also, die Tawmas-Technik für große Tische und die obige Technik für kleine Tische zu verwenden, wobei die Definition von groß und klein dem Leser überlassen bleibt (grins).

Dale Wilson
quelle

Antworten:

132

Sie müssen die Reihenfolge im Teilnehmermodell wie folgt angeben. Zum Beispiel (vorausgesetzt, Ihre Modellklasse heißt Teilnehmer):

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

Weitere Informationen finden Sie im Handbuch .

BEARBEITEN . Eine andere Lösung besteht darin, Ihrem Ereignismodell eine Eigenschaft hinzuzufügen, auf die Sie über Ihre Vorlage zugreifen können:

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

Sie können mehr davon definieren, wenn Sie sie benötigen ...

Tawmas
quelle
Vielen Dank für Ihren Kommentar, aber dies funktioniert nur, wenn ich die Anzeigereihenfolge zu einer dauerhaften Eigenschaft des Teilnehmers machen möchte, was ich nicht tue. Ich möchte beispielsweise die Teilnehmer nach dem Datum des Eingangs ihrer Registrierung sortiert anzeigen, damit ich weiß, welche Teilnehmer benachrichtigt werden sollten, dass kein Platz für sie vorhanden ist.
Dale Wilson
Ich habe eine alternative Lösung für Sie hinzugefügt. Es sollte Ihnen die Flexibilität ermöglichen, die Sie benötigen.
Tawmas
@ Mark In der Tat tut es. Soweit ich weiß , @propertyist es hier übertrieben, da keine Getter oder Setter beteiligt sind: stackoverflow.com/questions/1554546/…
Rick Westera
Obwohl dies für die Vorlage nicht unbedingt erforderlich ist, stelle ich fest, dass die @ -Eigenschaft hier einen saubereren Zugriff über den Anwendungscode ermöglicht , obwohl dies einen kleinen Leistungsverlust mit sich bringt (<30 ns auf meinem Laptop). Dies ist natürlich eine Frage des Stils und sehr subjektiv.
Tawmas
Wenn Sie die Menge nach zwei Attributen sortieren möchten, ist dies der Befehl: class Meta: ordering = ['Nachname', 'Vorname']
Tms91
128

Sie können den Vorlagenfilter dictsort https://docs.djangoproject.com/de/dev/ref/templates/builtins/#std:templatefilter-dictsort verwenden

Das sollte funktionieren:

{% 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
quelle
20
Toll ! Und für diejenigen, die sich fragen, gibt es auch dictsortreversed: docs.djangoproject.com/de/dev/ref/templates/builtins/…
Mickaël
1
Um aus meinem ursprünglichen Beitrag zu zitieren: Ah, wenn nur ein Abfragesatz ein Wörterbuch wäre, würde Dictsort den Job machen, aber es ist nicht und es ist nicht.
Dale Wilson
Ich denke, dies sollte in der Praxis weniger sein, und lassen Sie das Modell die Sortierung übernehmen, bevor Sie die Datensätze abrufen.
Acpmasquerade
1
@ DaleWilson, ich habe tatsächlich dictsortrichtig mit Code gearbeitet, fast genau wie bei Ihnen. Interessanterweise scheint es bei Abfragesätzen gut zu funktionieren.
mlissner
2
Und nur zur weiteren Klarheit sind Leerzeichen wichtig: {% for attendee in event.attendee_set.all|dictsort:"last_name" %}Sortiert die Teilnehmer, {% for attendee in event.attendee_set.all | dictsort:"last_name" %}versucht jedoch, die Ausgabe der for-Schleife zu sortieren, und unterbricht die for.
Mattsl
3

Eine Lösung besteht darin, ein benutzerdefiniertes Templatag zu erstellen:

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

Verwendung wie folgt:

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

regroup sollte in der Lage sein zu tun, was Sie wollen, aber gibt es einen Grund, warum Sie sie nicht so bestellen können, wie Sie es möchten?

Tom
quelle
Um sie in der Ansicht zu ordnen, müsste ich die Ereignisse durchlaufen und für jedes Ereignis einen Container mit einer Art von Teilnehmern erstellen, die mit diesem Ereignis in ordnungsgemäß sortierter Reihenfolge zusammenhängen, und dann die gesamte Sammlung von Containern in einer Form an die Vorlage übergeben Die Vorlage würde die entsprechende Sammlung von Teilnehmern finden, während sie die Ereignisse durchläuft. Wie gesagt, es könnte getan werden, aber es ist keine gute Lösung. Es erfordert viel zu viel Kopplung zwischen der Ansicht und der Vorlage, parallele Iterationen usw. Ich hatte gehofft, eine bessere Lösung für das zu finden, was ein häufiges Problem sein sollte.
Dale Wilson
Ich schaute auf Umgruppieren und es schien nicht zu tun, was ich wollte. In der Dokumentation heißt es insbesondere: [quote] Beachten Sie, dass {% regroup%} seine Eingabe nicht ordnet! Unser Beispiel basiert auf der Tatsache, dass die Personenliste in erster Linie nach Geschlecht geordnet wurde. [endquote]
Dale Wilson