Django ORM версия SQL COUNT (DISTINCT <столбец>)

Мне нужно заполнить шаблон сводкой активности пользователя в простой системе обмена сообщениями. Для каждого отправителя сообщения я хочу, чтобы количество отправленных сообщений и количество отдельных получателей.

Здесь приведен упрощенный вариант модели:

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)

Вот как я сделал бы это в SQL:

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

Я читал документацию по агрегации в запросах ORM, и хотя annotate() может обрабатывать первый столбец COUNT, я не вижу способа получить результат COUNT (DISTINCT) (даже extra (select = {}) не работает, хотя кажется, что он должен). Может ли это быть переведено в запрос ORM Django или я должен просто придерживаться необработанного SQL?

Ответ 1

Вы можете использовать разные и подсчеты вместе, как видно из этого ответа: fooobar.com/questions/169921/...

В вашем случае:

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

станет:

Message.objects.values('sender').annotate(
    message_count=Count('sender'),
    recipient_count=Count('recipient', distinct=True))

Ответ 2

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())