如何预取计数并使用它来在Django ORM

时间:2017-03-24 18:56:56

标签: python sql django orm

我正在尝试执行高级SQL查询,但我无法弄清楚如何执行此操作。 我的目标是为论坛实现一个读/未读系统,所以我有以下模型(不完整的: 简洁)

class ReadActivity(models.Model):
    content_object = GenericForeignKey('content_type', 'object_id')
    reader = models.ForeignKey(User, null=False, blank=False)
    read_until = models.DateTimeField(blank=False, null=False, default=timezone.now)
    class Meta:
        unique_together = (("content_type", "object_id", "reader"),)

class Readable(models.Model):
    read_activities = GenericRelation(ReadActivity)
    class Meta:
        abstract = True

class Forum(models.Model):
    pass
class Topic(Readable):
    pass

我认为这是非常自我解释的:主题可以标记为已读。用户每个主题具有零个或一个ReadActivity。 (事实上​​论坛也是可读的,但这个问题超出了这个问题)

所以:我希望能够获取论坛列表,并为每个论坛确定它是读取还是未读取(对于给定用户) 我的方法是: 对于每个论坛,如果任何主题没有任何ReadActivity或ReadActivity,其中read_until< last_message,将论坛标记为未读。

说到SQL,我会用以下方式做到这一点:

SELECT COUNT("forums_topic"."id")
FROM "forums_topic"
  LEFT JOIN "reads_readactivity"
    ON ("forums_topic"."id" = "reads_readactivity"."object_id" AND "reads_readactivity"."content_type_id" = '11')
WHERE (("reads_readactivity"."id" IS NULL
        OR ("reads_readactivity"."reader_id" = '1' AND "reads_readactivity"."read_until" < "forums_topic”."last_message_date"))
       AND "forums_topic"."forum_id" IN ('1', '2'))
GROUP BY "forums_topic"."forum_id"

但是我找不到使用Django ORM(没有很多查询)的方法。我试图使用Prefetch:

topics_prefetch = Topic.objects.filter(Q(read_activities=None) | (Q(read_activities__reader=self.request.user) & Q(read_activities__read_until__gt=F('created_at'))))
queryset = queryset.prefetch_related(Prefetch('read_activities', queryset=ReadActivity.objects.filter(reader=self.request.user)))

但是这个解决方案还不够好,因为我无法选择COUNT()或限制为1个结果,并按论坛对计数进行分组。

我尝试将此解决方案与注释结合使用:

queryset = queryset.annotate(is_unread=Count('topics__read_activities’))

但这只是不起作用,它会计算所有主题

然后我尝试在case / when:

中使用annotate
queryset = queryset.annotate(is_unread=Case(
    When(topics=None, then=False),
    When(topics__read_activities=None, then=True),
    When(topics__read_activities__reader=self.request.user, topics__read_activities__read_until__lte=F('topics__created_at'), then=True),
    default=False,
    output_field=BooleanField()
))

但它没有给出正确的结果。而且我不确定这是最好的解决方案。我不确定如何实现CASE / WHEN SQL,但如果它在内部迭代每个主题,这可能太贵了

现在我正在考虑编写RawSQL,但是我不知道如何添加一个管理器方法,它可以做类似的事情:Forums.objects.filter()。with_unread(),因为我不知道如何访问过滤的论坛以构建一个类似的查询 &#34; forums_topic&#34;&#34; forum_id&#34; IN(&#39; 1&#39;,&#39; 2&#39;))  来自经理本身。

也许我错过了什么,也许你知道怎么做。任何帮助将不胜感激,谢谢!

0 个答案:

没有答案