【问题标题】:Sum of related objects across 2 FK to the same table, with conditions跨 2 个 FK 到同一张表的相关对象的总和,有条件
【发布时间】:2019-10-10 07:32:19
【问题描述】:

我有两个模型:

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 的情况下做到这一点?

【问题讨论】:

  • 我认为您应该使用 django 的条件表达式,您可以在 django documentation 中所述的过滤器、注释、聚合和更新中使用 if elif else 逻辑
  • @dipbazz 尝试了很多,但无法让它工作。问题是他们生成了两个 JOINs 而不是一个子查询,这导致行数比实际多。

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


【解决方案1】:

我们可以在这里使用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

【讨论】:

  • 好的,这看起来可以工作(我不知道Subquery,谢谢!),但是对于实际数据,查询会引发错误:ERROR: more than one row returned by a subquery used as an expression
  • @frnhr:你是否在子查询参数的末尾添加了.values('pk')
  • 是的,我做到了。查询本身阻塞(直接在数据库上测试,没有 python)
  • @frnhr:啊,我找到了规避的方法。
猜你喜欢
  • 2012-12-29
  • 2016-10-12
  • 2021-03-02
  • 2010-12-19
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多