带有条件的跨2 FK到同一表的相关对象的总和

时间:2019-05-23 17:11:49

标签: django django-models django-queryset django-orm

我有两个模型:

class User(Model):
    ...

class Message(Model):
    sender = ForeignKey(User, CASCADE, 'sent_msgs')
    receiver = ForeignKey(User, CASCADE, 'rcvd_msgs')
    ignored = BooleanField()

我正在尝试用用户的相关消息的总和(即sent_msgsrcvd_msgs的总和来注释用户的查询集。此外,任何带有ignored=True的消息都应被忽略。

我可以使用子查询使用RawSQL来简单地做到这一点:

SELECT COUNT("messages_message"."id")
FROM "messages_message"
WHERE "messages_message"."ignored" = FALSE
  AND (
    "messages_message"."sender_id" = "users_user"."id"
    OR
    "messages_message"."receiver_id" = "users_user"."id"
  )
queryset = queryset.annotate(msgs_count=RawSQL(that_query_above))

是否有一种无需使用RawSQL的方法?

1 个答案:

答案 0 :(得分:2)

我们可以在此处使用Subquery [Django-doc]

from django.db.models import Count, OuterRef, Subquery, Q

User.objects.annotate(
    msgs_count=Subquery(
        Message.objects.filter(
            Q(sender_id=OuterRef('pk')) | Q(receiver_id=OuterRef('pk')),
            ignored=False
        ).order_by().values('ignored').annotate(cn=Count('*')).values('cn')
    )
)

然后产生一个查询,如:

SELECT auth_user.*,
    (
        SELECT COUNT(*) AS cn
        FROM message U0
        WHERE (U0.sender_id = auth_user.id OR U0.receiver_id = auth_user.id)
            AND U0.ignored = False)
        GROUP BY U0.ignored
    ) AS msgs_count
FROM auth_user