From e83ef7332d7fc22aa4d6e7e37f5e4d884ec72af1 Mon Sep 17 00:00:00 2001 From: Nikhil <118773738+pablohashescobar@users.noreply.github.com> Date: Mon, 24 Jul 2023 12:23:34 +0530 Subject: [PATCH] fix: issue create update n+1 and issue activity get n+1 (#1606) * fix: issue create update n+1 and issue activity get n+1 * dev: notifications n+1 --- apiserver/plane/api/serializers/issue.py | 94 +++++++++++++---------- apiserver/plane/api/views/issue.py | 39 ++++++---- apiserver/plane/api/views/notification.py | 10 ++- 3 files changed, 87 insertions(+), 56 deletions(-) diff --git a/apiserver/plane/api/serializers/issue.py b/apiserver/plane/api/serializers/issue.py index 6a8ccee84..4af53b49a 100644 --- a/apiserver/plane/api/serializers/issue.py +++ b/apiserver/plane/api/serializers/issue.py @@ -115,8 +115,15 @@ class IssueCreateSerializer(BaseSerializer): labels = validated_data.pop("labels_list", None) blocks = validated_data.pop("blocks_list", None) - project = self.context["project"] - issue = Issue.objects.create(**validated_data, project=project) + project_id = self.context["project_id"] + workspace_id = self.context["workspace_id"] + default_assignee_id = self.context["default_assignee_id"] + + issue = Issue.objects.create(**validated_data, project_id=project_id) + + # Issue Audit Users + created_by_id = issue.created_by_id + updated_by_id = issue.updated_by_id if blockers is not None and len(blockers): IssueBlocker.objects.bulk_create( @@ -124,10 +131,10 @@ class IssueCreateSerializer(BaseSerializer): IssueBlocker( block=issue, blocked_by=blocker, - project=project, - workspace=project.workspace, - created_by=issue.created_by, - updated_by=issue.updated_by, + project_id=project_id, + workspace_id=workspace_id, + created_by_id=created_by_id, + updated_by_id=updated_by_id, ) for blocker in blockers ], @@ -140,10 +147,10 @@ class IssueCreateSerializer(BaseSerializer): IssueAssignee( assignee=user, issue=issue, - project=project, - workspace=project.workspace, - created_by=issue.created_by, - updated_by=issue.updated_by, + project_id=project_id, + workspace_id=workspace_id, + created_by_id=created_by_id, + updated_by_id=updated_by_id, ) for user in assignees ], @@ -151,14 +158,14 @@ class IssueCreateSerializer(BaseSerializer): ) else: # Then assign it to default assignee - if project.default_assignee is not None: + if default_assignee_id is not None: IssueAssignee.objects.create( - assignee=project.default_assignee, + assignee_id=default_assignee_id, issue=issue, - project=project, - workspace=project.workspace, - created_by=issue.created_by, - updated_by=issue.updated_by, + project_id=project_id, + workspace_id=workspace_id, + created_by_id=created_by_id, + updated_by_id=updated_by_id, ) if labels is not None and len(labels): @@ -167,10 +174,10 @@ class IssueCreateSerializer(BaseSerializer): IssueLabel( label=label, issue=issue, - project=project, - workspace=project.workspace, - created_by=issue.created_by, - updated_by=issue.updated_by, + project_id=project_id, + workspace_id=workspace_id, + created_by_id=created_by_id, + updated_by_id=updated_by_id, ) for label in labels ], @@ -183,10 +190,10 @@ class IssueCreateSerializer(BaseSerializer): IssueBlocker( block=block, blocked_by=issue, - project=project, - workspace=project.workspace, - created_by=issue.created_by, - updated_by=issue.updated_by, + project_id=project_id, + workspace_id=workspace_id, + created_by_id=created_by_id, + updated_by_id=updated_by_id, ) for block in blocks ], @@ -201,6 +208,12 @@ class IssueCreateSerializer(BaseSerializer): labels = validated_data.pop("labels_list", None) blocks = validated_data.pop("blocks_list", None) + # Related models + project_id = instance.project_id + workspace_id = instance.workspace_id + created_by_id = instance.created_by_id + updated_by_id = instance.updated_by_id + if blockers is not None: IssueBlocker.objects.filter(block=instance).delete() IssueBlocker.objects.bulk_create( @@ -208,10 +221,10 @@ class IssueCreateSerializer(BaseSerializer): IssueBlocker( block=instance, blocked_by=blocker, - project=instance.project, - workspace=instance.project.workspace, - created_by=instance.created_by, - updated_by=instance.updated_by, + project_id=project_id, + workspace_id=workspace_id, + created_by_id=created_by_id, + updated_by_id=updated_by_id, ) for blocker in blockers ], @@ -225,10 +238,10 @@ class IssueCreateSerializer(BaseSerializer): IssueAssignee( assignee=user, issue=instance, - project=instance.project, - workspace=instance.project.workspace, - created_by=instance.created_by, - updated_by=instance.updated_by, + project_id=project_id, + workspace_id=workspace_id, + created_by_id=created_by_id, + updated_by_id=updated_by_id, ) for user in assignees ], @@ -242,10 +255,10 @@ class IssueCreateSerializer(BaseSerializer): IssueLabel( label=label, issue=instance, - project=instance.project, - workspace=instance.project.workspace, - created_by=instance.created_by, - updated_by=instance.updated_by, + project_id=project_id, + workspace_id=workspace_id, + created_by_id=created_by_id, + updated_by_id=updated_by_id, ) for label in labels ], @@ -259,16 +272,17 @@ class IssueCreateSerializer(BaseSerializer): IssueBlocker( block=block, blocked_by=instance, - project=instance.project, - workspace=instance.project.workspace, - created_by=instance.created_by, - updated_by=instance.updated_by, + project_id=project_id, + workspace_id=workspace_id, + created_by_id=created_by_id, + updated_by_id=updated_by_id, ) for block in blocks ], batch_size=10, ) + # Time updation occues even when other related models are updated instance.updated_at = timezone.now() return super().update(instance, validated_data) diff --git a/apiserver/plane/api/views/issue.py b/apiserver/plane/api/views/issue.py index 02c8ed46a..fb98676a4 100644 --- a/apiserver/plane/api/views/issue.py +++ b/apiserver/plane/api/views/issue.py @@ -270,9 +270,15 @@ class IssueViewSet(BaseViewSet): def create(self, request, slug, project_id): try: - project = Project.objects.get(workspace__slug=slug, pk=project_id) + project = Project.objects.get(pk=project_id) + serializer = IssueCreateSerializer( - data=request.data, context={"project": project} + data=request.data, + context={ + "project_id": project_id, + "workspace_id": project.workspace_id, + "default_assignee_id": project.default_assignee_id, + }, ) if serializer.is_valid(): @@ -396,6 +402,7 @@ class IssueActivityEndpoint(BaseAPIView): IssueComment.objects.filter(issue_id=issue_id) .filter(project__project_projectmember__member=self.request.user) .order_by("created_at") + .select_related("actor", "issue", "project", "workspace") ) issue_activities = IssueActivitySerializer(issue_activities, many=True).data issue_comments = IssueCommentSerializer(issue_comments, many=True).data @@ -1096,7 +1103,8 @@ class IssueArchiveViewSet(BaseViewSet): return Response(IssueSerializer(issue).data, status=status.HTTP_200_OK) except Issue.DoesNotExist: return Response( - {"error": "Issue Does not exist"}, status=status.HTTP_404_NOT_FOUND) + {"error": "Issue Does not exist"}, status=status.HTTP_404_NOT_FOUND + ) except Exception as e: capture_exception(e) return Response( @@ -1104,6 +1112,7 @@ class IssueArchiveViewSet(BaseViewSet): status=status.HTTP_400_BAD_REQUEST, ) + class IssueSubscriberViewSet(BaseViewSet): serializer_class = IssueSubscriberSerializer model = IssueSubscriber @@ -1144,18 +1153,22 @@ class IssueSubscriberViewSet(BaseViewSet): def list(self, request, slug, project_id, issue_id): try: - members = ProjectMember.objects.filter( - workspace__slug=slug, project_id=project_id - ).annotate( - is_subscribed=Exists( - IssueSubscriber.objects.filter( - workspace__slug=slug, - project_id=project_id, - issue_id=issue_id, - subscriber=OuterRef("member"), + members = ( + ProjectMember.objects.filter( + workspace__slug=slug, project_id=project_id + ) + .annotate( + is_subscribed=Exists( + IssueSubscriber.objects.filter( + workspace__slug=slug, + project_id=project_id, + issue_id=issue_id, + subscriber=OuterRef("member"), + ) ) ) - ).select_related("member") + .select_related("member") + ) serializer = ProjectMemberLiteSerializer(members, many=True) return Response(serializer.data, status=status.HTTP_200_OK) except Exception as e: diff --git a/apiserver/plane/api/views/notification.py b/apiserver/plane/api/views/notification.py index 83fe0a9aa..393a3c9ad 100644 --- a/apiserver/plane/api/views/notification.py +++ b/apiserver/plane/api/views/notification.py @@ -38,9 +38,13 @@ class NotificationViewSet(BaseViewSet, BasePaginator): # Filter type type = request.GET.get("type", "all") - notifications = Notification.objects.filter( - workspace__slug=slug, receiver_id=request.user.id - ).order_by("snoozed_till", "-created_at") + notifications = ( + Notification.objects.filter( + workspace__slug=slug, receiver_id=request.user.id + ) + .select_related("workspace", "project," "triggered_by", "receiver") + .order_by("snoozed_till", "-created_at") + ) # Filter for snoozed notifications if snoozed == "false":