diff --git a/CHANGELOG.md b/CHANGELOG.md index 9dd8ba06c0..5d410d0166 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,10 @@ and this project adheres to - ♿ add missing aria-label to add sub-doc button for accessib… #1480 - ♿ add missing aria-label to more options button on sub-docs #1481 +### Fixed + +- ⚡️(backend) improve trashbin endpoint performance + ## [3.8.0] - 2025-10-14 ### Added diff --git a/src/backend/core/api/viewsets.py b/src/backend/core/api/viewsets.py index 2cd45e6233..009d96839e 100644 --- a/src/backend/core/api/viewsets.py +++ b/src/backend/core/api/viewsets.py @@ -623,12 +623,29 @@ def trashbin(self, request, *args, **kwargs): The selected documents are those deleted within the cutoff period defined in the settings (see TRASHBIN_CUTOFF_DAYS), before they are considered permanently deleted. """ + + if not request.user.is_authenticated: + return self.get_response_for_queryset(self.queryset.none()) + + access_documents_paths = ( + models.DocumentAccess.objects.select_related("document") + .filter( + db.Q(user=self.request.user) | db.Q(team__in=self.request.user.teams), + role=models.RoleChoices.OWNER, + ) + .values_list("document__path", flat=True) + ) + + children_clause = db.Q() + for path in access_documents_paths: + children_clause |= db.Q(path__startswith=path) + queryset = self.queryset.filter( + children_clause, deleted_at__isnull=False, deleted_at__gte=models.get_trashbin_cutoff(), ) queryset = queryset.annotate_user_roles(self.request.user) - queryset = queryset.filter(user_roles__contains=[models.RoleChoices.OWNER]) return self.get_response_for_queryset(queryset) diff --git a/src/backend/core/tests/documents/test_api_documents_trashbin.py b/src/backend/core/tests/documents/test_api_documents_trashbin.py index fbcc23175c..ffdffd4314 100644 --- a/src/backend/core/tests/documents/test_api_documents_trashbin.py +++ b/src/backend/core/tests/documents/test_api_documents_trashbin.py @@ -166,10 +166,10 @@ def test_api_documents_trashbin_authenticated_direct(django_assert_num_queries): expected_ids = {str(document1.id), str(document2.id), str(document3.id)} - with django_assert_num_queries(10): + with django_assert_num_queries(11): response = client.get("/api/v1.0/documents/trashbin/") - with django_assert_num_queries(4): + with django_assert_num_queries(5): response = client.get("/api/v1.0/documents/trashbin/") assert response.status_code == 200 @@ -208,10 +208,10 @@ def test_api_documents_trashbin_authenticated_via_team( expected_ids = {str(deleted_document_team1.id), str(deleted_document_team2.id)} - with django_assert_num_queries(7): + with django_assert_num_queries(8): response = client.get("/api/v1.0/documents/trashbin/") - with django_assert_num_queries(3): + with django_assert_num_queries(4): response = client.get("/api/v1.0/documents/trashbin/") assert response.status_code == 200