2009-08-06 10 views
13

Ma question est presque exactement la même que this post, sauf que j'utilise Python et Django au lieu de PHP.Regrouper des dates dans Django

La tâche est de prendre:

id date 
1 2009-01-01 10:15:23 
2 2009-01-01 13:21:29 
3 2009-01-02 01:03:13 
4 2009-01-03 12:20:19 
5 2009-01-03 13:01:06 

Et sortie:

2009-01-01 
1 
2 
2009-01-02 
3 
2009-01-03 
4 
5 

Je peux pirater manuellement quelque chose ensemble en boucle à travers les dates triées et la sortie HTML dans une chaîne dans mon fichier de vue python , mais je me demande: est-il un meilleur moyen d'utiliser un modèle Django ou une fonctionnalité Python astucieuse?

Répondre

30

Vous pouvez résoudre ce problème avec le regrouper et date templatetags.

{% regroup object_list by start_date|date:"Y-m-d" as objects_by_day %} 
{% for d in objects_by_day %} 
### DO SOMETHING HERE 
{% endfor %} 
+0

J'étais la plupart du temps là mais je n'ai pas réalisé que vous pouviez mélanger le filtre de date. Merci beaucoup pour ça! Exactement ce que je cherchais. – saturdayplace

+0

bon dieu, comment je ne sais pas à ce sujet –

+0

il est plus rapide d'utiliser start_date.date si vous avez besoin de la «partie de la date» –

2

Cela serait plus facile si vous utilisiez les fonctions de formatage de la date de votre base de données pour supprimer les heures (dans la sélection). Vous pouvez ensuite regrouper par la colonne formatée (en utilisant un alias de colonne). Après cela, vous pouvez utiliser une boucle avec un peu de logique pour transposer les résultats à ce que vous voulez. Je ne l'ai pas encore utilisé le système de template de django, mais voici ce que la logique ressemblerait en Python:

>>> rows = [(1,'2009-01-01'), (2,'2009-01-01'), (3, '2009-02-02'), \ 
(4, '2009-02-02'), (5, '2009-02-02')] 
>>> topdate = None 
>>> for r in rows: 
...  if topdate <> r[1]: 
...    topdate = r[1] 
...    print r[1] 
...  print r[0] 
... 
2009-01-01 
1 
2 
2009-02-02 
3 
4 
5 

Il devrait y avoir un moyen simple pour convertir en django code modèle.

+0

Je peux regrouper les données, mais à quoi ressemblerait la "petite logique" à l'intérieur du système de gabarit? – ine

+0

J'ai ajouté un exemple. –

+0

Je ne suis pas convaincu que vous pouvez le faire dans les templates Django. Même si vous le pouviez, vous ne le voudriez pas. Il est préférable de le faire dans: (1) le code de vue ou (2) un modèle de site où vous pouvez utiliser le vrai python. – hughdbrown

15

itertools.groupby est votre cher, cher ami:

import itertools 

dates = [ 
    (1,'2009-01-01 10:15:23'), 
    (2,'2009-01-01 13:21:29'), 
    (3,'2009-01-02 01:03:13'), 
    (4,'2009-01-03 12:20:19'), 
    (5,'2009-01-03 13:01:06'), 
] 

for key,group in itertools.groupby(dates, key=lambda x: x[1][:11]): 
    print key 
    for element in group: 
     print ' ', element 

Le code ci-dessus imprime les suivantes:

2009-01-01 
    (1, '2009-01-01 10:15:23') 
    (2, '2009-01-01 13:21:29') 
2009-01-02 
    (3, '2009-01-02 01:03:13') 
2009-01-03 
    (4, '2009-01-03 12:20:19') 
    (5, '2009-01-03 13:01:06') 
+0

Excellent! itertools.groupby est exactement ce que je cherchais. – ine

+0

Si vous allez le faire dans la vue, vous voulez quelque chose comme ceci: « def sidebar_date_list(): de itertools importer GroupBy data = [datetime.date (post.created_at.year, post.created_at.month , 1) pour l'article dans Post.objects.filter (publish = 1) .order_by ('- created_at')] return {'months': [k pour k, _ dans groupby (data)]} Notez que le SQL doit être trié pour que groupby() l'utilise. " Soulevé d'ici: http://www.djangrrl.com/view/using-python-str-datetime-lists-and-sets-to-group-dates/ – hughdbrown

+0

Ou comme ceci si vous voulez regrouper par jours: " def sidebar_date_list(): \t depuis itertools importer groupby \t data = [datetime.date (post.created_at.year, post.created_at.month, post.created_at.day) pour publication dans Post.objects.filter (publish = 1) .order_by ('- created_at')] \t return {'days': [k pour k, _ dans groupby (data)]} " – hughdbrown

8

Ce sont toutes les réponses raisonnables si l'OP pourrait utiliser python dans le code du modèle. (Et pour ce compte, je favoriserais la solution itertools.groupby().) Mais vous ne pouvez pas utiliser python dans le code du template. Dans le meilleur des cas, vous écririez un template qui a effectué cette opération. Et il y a déjà un templatetag qui fait cela.

Pour faire le regroupement dans Django à l'intérieur du modèle, utilisez regroup:

{% regroup people by gender as gender_list %} 

<ul> 
{% for gender in gender_list %} 
    <li>{{ gender.grouper }} 
    <ul> 
     {% for item in gender.list %} 
     <li>{{ item.first_name }} {{ item.last_name }}</li> 
     {% endfor %} 
    </ul> 
    </li> 
{% endfor %} 
</ul> 

Exemple de Django regroup documentation soulevé. regroupement est disponible en django 0.96, 1.0, et 1.1, au moins.

Si vous me donnez une idée de ce qui est passé au modèle, je pourrais pirater un exemple qui utilise vos données.

De plus, il y a un blog post dans lequel DjangoGrrl travaille exactement à cette question.

+0

Les langages de gabarit ont tendance à fournir assez de constructions de langage identiques que vous pouvez facilement convertir entre les deux (ils partagent le même bytecode cible). –

+0

Ne pas être impoli, mais programmez-vous dans Django? Pour des raisons pratiques, il n'y a que des comparaisons et des opérations d'assignation rudimentaires. Découvrez ce qui est disponible sans utiliser templatetags: http://docs.djangoproject.com/fr/dev/topics/templates/ – hughdbrown

+0

Malheureusement, j'utilise 0,96 mais c'est une excellente ressource pour ceux sur 1.0 ou plus! – ine

9

Lorsque vous effectuez des rapports sur des ensembles de données plus volumineux, itertools.group_by peut être trop lent. Dans ce cas je fais gérer le groupage par postgres:

truncate_date = connection.ops.date_trunc_sql('day','timestamp') 
qs = qs.extra({'date':truncate_date}) 
return qs.values('date').annotate(Sum('amount')).order_by('date') 
Questions connexes