2013-02-12 15 views
10

Muszę wypełnić szablon podsumowaniem aktywności użytkownika w prostym systemie przesyłania wiadomości. Dla każdego nadawcy wiadomości chcę liczbę wysłanych wiadomości i liczbę różnych odbiorców.Django ORM wersja SQL COUNT (DISTINCT)

Oto uproszczona wersja modelu:

class Message(models.Model): 
    sender = models.ForeignKey(User, related_name='messages_from') 
    recipient = models.ForeignKey(User, related_name='messages_to') 
    timestamp = models.DateTimeField(auto_now_add=True) 

Oto jak zrobiłbym to w SQL:

SELECT sender_id, COUNT(id), COUNT(DISTINCT recipient_id) 
    FROM myapp_messages 
    GROUP BY sender_id; 

Czytałem poprzez dokumentację na agregację w zapytaniach ORM, i chociaż funkcja annotate() może obsłużyć pierwszą kolumnę COUNT, nie widzę sposobu na uzyskanie wyniku COUNT (DISTINCT) (nawet dodatkowy (select = {}) nie działa, chociaż wydaje się, że powinien). Czy można to przetłumaczyć na zapytanie ORM Django, czy powinienem trzymać się surowego kodu SQL?

+0

Sieć [ ' .distinct() '] (https://docs.djangoproject.com/en/dev/ref/models/querysets/#django.db.models.query.QuerySet.distinct) filter could com e przydaje się. –

+0

Jeśli zignorujemy COUNT (DISTINCT recipient_id), jest to całkiem proste dzięki funkcji adnotacji i liczenia. W tym przypadku django's distinct() nie pomaga. Nie sądzę, że możliwe jest zintegrowanie tych dwóch rzeczy (opis i odrębność) razem. Musisz chyba napisać dwa zapytania. – jurgenreza

Odpowiedz

8

Można rzeczywiście wykorzystywać odrębne i liczyć razem, jak widać na tej odpowiedzi: https://stackoverflow.com/a/13145407/237091

W twoim przypadku:

SELECT sender_id, COUNT(id), COUNT(DISTINCT recipient_id) 
FROM myapp_messages 
GROUP BY sender_id; 

staną:

Message.objects.values('sender').annotate(
    message_count=Count('sender'), 
    recipient_count=Count('recipient', distinct=True)) 
4
from django.db.models import Count 

messages = Message.objects.values('sender').annotate(message_count=Count('sender')) 

for m in messages: 
    m['recipient_count'] = len(Message.objects.filter(sender=m['sender']).\ 
           values_list('recipient', flat=True).distinct()) 
+0

Dziękujemy! Czy mam rację, rozumiejąc, że oznacza to, że będzie tyle zapytań do bazy danych, ile jest nadawców wiadomości (plus jeden)? – nephtes

+0

@nephtes Tak. Zasadniczo nie sądzę, że istnieje sposób użycia distinct() wewnątrz Count(). Być może, lepiej się z raw sql. Myślę, że jest to najlepszy sposób na zrobienie tego z django, chyba że inni wymyślą lepszy pomysł! – jurgenreza

+1

@jurgenreza (Wiem, że ten post jest stary, ale ...) Wierzę, że lepszym sposobem byłoby użycie dodatkowego: Message.objects.filter (...). Extra ({'recipient_count': 'COUNT (DISTINCT recipient_id) '}). values ​​(' recipient_count '). Dokumenty na dodatkowe: https://docs.djangoproject.com/en/dev/ref/models/querysets/#django.db.models.query.QuerySet.extra – paperreduction

Powiązane problemy