106 votes

Trier les éléments connexes dans un modèle Django

Est-il possible de trier un ensemble d'éléments connexes dans un modèle DJango ?

C'est-à-dire : ce code (avec les balises HTML omises pour plus de clarté) :

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

affichages presque exactement ce que je veux. La seule chose que je souhaite modifier est que la liste des participants soit triée par nom de famille. J'ai essayé de dire quelque chose comme ceci :

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

Hélas, la syntaxe ci-dessus ne fonctionne pas (elle produit une liste vide), pas plus que les autres variantes auxquelles j'ai pensé (de nombreuses erreurs de syntaxe sont signalées, mais rien ne se passe).

Je pourrais, bien sûr, produire une sorte de tableau de listes de participants triées dans ma vue, mais c'est une solution laide et fragile (et ai-je mentionné la laideur).

Inutile de dire, mais je vais le dire quand même, que j'ai parcouru la documentation en ligne et cherché sur Stack Overflow et dans les archives de django-user sans rien trouver d'utile (ah, si seulement un ensemble de requêtes était un dictionnaire dictsort ferait l'affaire, mais ce n'est pas le cas et ça ne marche pas).

\==============================================

Modification de l'article pour ajouter des réflexions supplémentaires après avoir accepté la réponse de Tawmas.


Tawmas a abordé le problème exactement comme je l'avais présenté, même si la solution n'était pas celle à laquelle je m'attendais. J'ai ainsi appris une technique utile qui peut être utilisée dans d'autres situations.

La réponse de Tom proposait une approche que j'avais déjà mentionnée dans mon OP et que j'avais provisoirement rejetée parce qu'elle était "laide".

Le "moche" était une réaction instinctive, et je voulais clarifier ce qui n'allait pas. Ce faisant, je me suis rendu compte que la raison pour laquelle cette approche était laide était que j'étais accroché à l'idée de passer un ensemble de requêtes au modèle pour qu'il soit rendu. Si je relâche cette exigence, il existe une approche non laide qui devrait fonctionner.

Je n'ai pas encore essayé, mais supposons qu'au lieu de transmettre l'ensemble de requêtes, le code de la vue itère à travers l'ensemble de requêtes pour produire une liste d'événements, puis décore chaque événement avec un ensemble de requêtes pour les participants correspondants qui WAS trié (ou filtré, ou autre) de la manière souhaitée. Quelque chose comme ça :

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)

Le modèle devient alors :

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

L'inconvénient est que la vue doit "actualiser" tous les événements en même temps, ce qui peut poser un problème s'il y a un grand nombre d'événements. Bien sûr, on peut ajouter la pagination, mais cela complique considérablement la vue.

L'avantage est que le code de "préparation des données à afficher" se trouve dans la vue, à sa place, ce qui permet au modèle de se concentrer sur le formatage des données fournies par la vue pour l'affichage. C'est une bonne chose.

J'ai donc l'intention d'utiliser la technique de Tawmas pour les grandes tables et la technique ci-dessus pour les petites. pour les petits tableaux, la définition de grand et petit étant laissée au lecteur (grincheux).

171voto

tariwan Points 131

Vous pouvez utiliser le filtre de modèle dictsort https://docs.djangoproject.com/en/dev/ref/templates/builtins/#std:templatefilter-dictsort

Cela devrait fonctionner :

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

157voto

tawmas Points 1512

Vous devez spécifier l'ordre dans le modèle de l'invité, comme suit. Par exemple (en supposant que votre classe modèle s'appelle Attendee) :

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

Voir le manuel pour plus d'informations.

EDIT . Une autre solution consiste à ajouter une propriété à votre modèle d'événement, à laquelle vous pouvez accéder à partir de votre modèle :

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

Vous pouvez en définir d'autres au fur et à mesure de vos besoins...

8voto

matinfo Points 21

Une solution consiste à créer un templatag personnalisé :

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

utiliser comme ceci :

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

0voto

Tom Points 7393

regrouper devrait pouvoir faire ce que vous voulez, mais y a-t-il une raison pour laquelle vous ne pouvez pas les commander comme vous le souhaitez dans la vue ?

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X