web-dev-qa-db-fra.com

Tri des éléments associés dans un modèle Django

Est-il possible de trier un ensemble d'éléments associés dans un modèle Django?

C'est-à-dire: ce code (avec des 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 %}

affiche presque exactement ce que je veux. La seule chose que je veux changer, c'est la liste des participants à trier par nom de famille. J'ai essayé de dire quelque chose comme ça:

{% 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) et aucune autre variation à laquelle j'ai pensé (beaucoup d'erreurs de syntaxe signalées, mais aucune joie).

Je pourrais, bien sûr, produire une sorte de liste de listes de participants triées à mon avis, mais c'est une solution laide et fragile (et ai-je mentionné laide).

Inutile de dire, mais je le dirai quand même, j'ai parcouru les documents en ligne et fouillé Stack Overflow et les archives de Django-user sans trouver quoi que ce soit d'utile (ah, si seulement un ensemble de requêtes était un dictionnaire, le Dictort ferait le travail, mais ce n'est pas et ce n'est pas le cas)

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

Modifié 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'ai présenté - bien que la solution ne soit pas ce que j'attendais. En conséquence, j'ai appris une technique utile qui peut également être utilisée dans d'autres situations.

La réponse de Tom proposait une approche que j'avais déjà mentionnée dans mon PO et provisoirement rejetée comme étant "laide".

Le "laid" était une réaction instinctive, et je voulais clarifier ce qui n'allait pas. Ce faisant, j'ai réalisé que la raison pour laquelle c'était une approche laide était parce que j'étais accroché à l'idée de passer un ensemble de requêtes au modèle à rendre. Si je relâche cette exigence, il y a une approche laide qui devrait fonctionner.

Je n'ai pas encore essayé cela, mais supposons qu'au lieu de passer l'ensemble de requêtes, le code de vue a itéré à travers l'ensemble de requêtes produisant une liste d'événements, puis a décoré chaque événement avec un ensemble de requêtes pour les participants correspondants qui [~ # ~] a été [~ # ~] 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)

Maintenant, le modèle devient:

{% 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 à la fois, ce qui pourrait être un problème s'il y avait un grand nombre d'événements. Bien sûr, on pourrait ajouter la pagination, mais cela complique considérablement la vue.

L'avantage est que le code "préparer les données à afficher" est dans la vue à laquelle il appartient, ce qui permet au modèle de se concentrer sur le formatage des données fournies par la vue pour l'affichage. C'est juste et approprié.

Mon plan est donc d'utiliser la technique de Tawmas pour les grandes tables et la technique ci-dessus pour les petites tables, avec la définition de grandes et petites laissées au lecteur (sourire).

75
Dale Wilson

Vous devez spécifier l'ordre dans le modèle de participant, comme ceci. Par exemple (en supposant que votre classe de modèle est nommée Participant):

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

Voir le manuel pour plus de référence.

[~ # ~] éditez [~ # ~] . 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 davantage selon vos besoins ...

116
tawmas

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 %}
103
tariwan

regroup 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?

0
Tom

Une solution consiste à créer un modèle 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 %}
0
matinfo