Alg*_*lgo 5 python sql django orm
我\xe2\x80\x99m 尝试执行高级 SQL 查询,但我无法\xe2\x80\x99t 弄清楚如何执行此操作。\n我的目标是为论坛实现读/未读系统,因此我有以下内容模型(不完整:\n简洁)
\n\nclass ReadActivity(models.Model):\n content_object = GenericForeignKey(\'content_type\', \'object_id\')\n reader = models.ForeignKey(User, null=False, blank=False)\n read_until = models.DateTimeField(blank=False, null=False, default=timezone.now)\n class Meta:\n unique_together = (("content_type", "object_id", "reader"),)\n\nclass Readable(models.Model):\n read_activities = GenericRelation(ReadActivity)\n class Meta:\n abstract = True\n\nclass Forum(models.Model):\n pass\nclass Topic(Readable):\n pass\nRun Code Online (Sandbox Code Playgroud)\n\n我认为这是非常不言自明的:主题可以标记为已读。用户每个主题有零个或一个 ReadActivity。(事实上,论坛也是可读的,但这超出了这个问题的范围)
\n\n所以:我希望能够获取论坛列表,并为每个论坛确定它是否已读或未读(对于给定用户)\n我的方法是:\n对于每个论坛,如果任何主题没有任何 ReadActivity 或ReadActivity where read_until < last_message,将论坛标记为未读。
\n\n说到 SQL,我会通过以下方式做到这一点:
\n\nSELECT COUNT("forums_topic"."id")\nFROM "forums_topic"\n LEFT JOIN "reads_readactivity"\n ON ("forums_topic"."id" = "reads_readactivity"."object_id" AND "reads_readactivity"."content_type_id" = \'11\')\nWHERE (("reads_readactivity"."id" IS NULL\n OR ("reads_readactivity"."reader_id" = \'1\' AND "reads_readactivity"."read_until" < "forums_topic\xe2\x80\x9d."last_message_date"))\n AND "forums_topic"."forum_id" IN (\'1\', \'2\'))\nGROUP BY "forums_topic"."forum_id"\nRun Code Online (Sandbox Code Playgroud)\n\n但我可以\xe2\x80\x99t找到一种使用Django ORM(没有很多很多查询)来做到这一点的方法。我尝试使用 Prefetch :
\n\ntopics_prefetch = Topic.objects.filter(Q(read_activities=None) | (Q(read_activities__reader=self.request.user) & Q(read_activities__read_until__gt=F(\'created_at\'))))\nqueryset = queryset.prefetch_related(Prefetch(\'read_activities\', queryset=ReadActivity.objects.filter(reader=self.request.user)))\nRun Code Online (Sandbox Code Playgroud)\n\n但这个解决方案还不够好,因为我无法选择 COUNT(),或限制为 1 个结果,并按论坛对计数进行分组。
\n\n我尝试将此解决方案与注释结合起来:
\n\nqueryset = queryset.annotate(is_unread=Count(\'topics__read_activities\xe2\x80\x99))\nRun Code Online (Sandbox Code Playgroud)\n\n但这不起作用,它计算所有主题
\n\n然后我尝试使用 case/when 进行注释:
\n\nqueryset = queryset.annotate(is_unread=Case(\n When(topics=None, then=False),\n When(topics__read_activities=None, then=True),\n When(topics__read_activities__reader=self.request.user, topics__read_activities__read_until__lte=F(\'topics__created_at\'), then=True),\n default=False,\n output_field=BooleanField()\n))\nRun Code Online (Sandbox Code Playgroud)\n\n但它没有给出正确的结果。此外,我\xe2\x80\x99m 不确定这是最好的解决方案。I\xe2\x80\x99m 不确定 CASE/WHEN SQL 是如何实现的,但如果它在每个主题上进行内部迭代,这可能会太昂贵
\n\n现在我\xe2\x80\x99m考虑编写RawSQL,但是我不\xe2\x80\x99t知道如何添加一个管理器方法,它可以让我们做类似的事情:Forums.objects.filter().with_unread(),因为我不\xe2\x80\x99不知道如何访问过滤的论坛以从管理器本身构建\n“forums_topic”.“forum_id”IN(\'1\',\'2\'))\n之类的查询。
\n\n也许我\xe2\x80\x99m缺少一些东西,也许你知道该怎么做。任何帮助将不胜感激,谢谢!
\n