我正在为我的网站制作一个非常简单的通知系统,由 Django REST Framework API 提供支持。它用于向所有用户发送网站更新和内容,每个人都会收到相同的通知,然后他们可以将其标记为已读/存档。我想出了以下模型:
class Notification(models.Model):
title = models.CharField(max_length=255)
text = models.TextField()
type = models.CharField(max_length=255, blank=True)
read_by = models.ManyToManyField(User, blank=True, related_name="read_notifications")
archived_by = models.ManyToManyField(User, blank=True, related_name="archived_notifications")
created_at = models.DateTimeField(auto_now_add=True, db_index=True)
updated_at = models.DateTimeField(auto_now=True)
所以没有接收器字段或类似的东西,因为无论如何所有用户都会收到所有通知。
现在我正在尝试编写视图逻辑,特别是以下两件事:仅获取创建用户后发出的非存档通知,并向其添加计算的“is_read”字段,以不执行额外操作的方式查询每个通知/用户组合。
查询现在看起来像这样:
queryset = Notification.objects
.order_by("-created_at")
.filter(created_at__gt=self.request.user.created_at)
.exclude(archived_by=self.request.user)
这确实按预期过滤掉了存档查询,而且我认为它不需要对每个通知进行额外的查询:
SELECT "notifications_notification"."id", "notifications_notification"."title", "notifications_notification"."text", "notifications_notification"."type", "notifications_notification"."created_at", "notifications_notification"."updated_at" FROM "notifications_notification" WHERE ("notifications_notification"."created_at" > 2022-09-26 12:44:04.771961+00:00 AND NOT (EXISTS(SELECT 1 AS "a" FROM "notifications_notification_archived_by" U1 WHERE (U1."user_id" = 1 AND U1."notification_id" = ("notifications_notification"."id")) LIMIT 1))) ORDER BY "notifications_notification"."created_at" DESC
到目前为止一切顺利!但我仍然需要以某种方式向查询添加“is_read”值(或“is_unread”,如果更容易的话),但我无法弄清楚该怎么做。
如何完成查询并使其高性能?
我想出了这个:
queryset = Notification.objects.order_by("-created_at")
.filter(created_at__gt=self.request.user.created_at)
.exclude(archived_by=self.request.user)
.annotate(is_read=Exists(Notification.objects.filter(pk=OuterRef("id"), read_by=self.request.user)))
这是可行的,尽管它确实执行了两个子查询,我想知道这是否会成为以后的瓶颈?
SELECT "notifications_notification"."id", "notifications_notification"."title", "notifications_notification"."text", "notifications_notification"."type", "notifications_notification"."created_at", "notifications_notification"."updated_at",
EXISTS(SELECT 1 AS "a" FROM "notifications_notification" U0 INNER JOIN "notifications_notification_read_by" U1 ON (U0."id" = U1."notification_id")
WHERE (U0."id" = ("notifications_notification"."id") AND U1."user_id" = 1) LIMIT 1) AS "is_read" FROM "notifications_notification"
WHERE ("notifications_notification"."created_at" > 2022-09-26 14:40:29.368043+00:00 AND NOT (EXISTS(SELECT 1 AS "a" FROM "notifications_notification_archived_by" U1
WHERE (U1."user_id" = 1 AND U1."notification_id" = ("notifications_notification"."id")) LIMIT 1)))
ORDER BY "notifications_notification"."created_at" DESC