Skip to content

Commit 149f711

Browse files
sampaccoudPanchoutNathan
authored andcommitted
♻️(backend) factorize document query set annotation
The methods to annotate a document queryset were factorized on the viewset but the correct place is the custom queryset itself now that we have one.
1 parent b5dad9e commit 149f711

File tree

2 files changed

+49
-48
lines changed

2 files changed

+49
-48
lines changed

src/backend/core/api/viewsets.py

Lines changed: 14 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -404,44 +404,6 @@ class DocumentViewSet(
404404
trashbin_serializer_class = serializers.ListDocumentSerializer
405405
tree_serializer_class = serializers.ListDocumentSerializer
406406

407-
def annotate_is_favorite(self, queryset):
408-
"""
409-
Annotate document queryset with the favorite status for the current user.
410-
"""
411-
user = self.request.user
412-
413-
if user.is_authenticated:
414-
favorite_exists_subquery = models.DocumentFavorite.objects.filter(
415-
document_id=db.OuterRef("pk"), user=user
416-
)
417-
return queryset.annotate(is_favorite=db.Exists(favorite_exists_subquery))
418-
419-
return queryset.annotate(is_favorite=db.Value(False))
420-
421-
def annotate_user_roles(self, queryset):
422-
"""
423-
Annotate document queryset with the roles of the current user
424-
on the document or its ancestors.
425-
"""
426-
user = self.request.user
427-
output_field = ArrayField(base_field=db.CharField())
428-
429-
if user.is_authenticated:
430-
user_roles_subquery = models.DocumentAccess.objects.filter(
431-
db.Q(user=user) | db.Q(team__in=user.teams),
432-
document__path=Left(db.OuterRef("path"), Length("document__path")),
433-
).values_list("role", flat=True)
434-
435-
return queryset.annotate(
436-
user_roles=db.Func(
437-
user_roles_subquery, function="ARRAY", output_field=output_field
438-
)
439-
)
440-
441-
return queryset.annotate(
442-
user_roles=db.Value([], output_field=output_field),
443-
)
444-
445407
def get_queryset(self):
446408
"""Get queryset performing all annotation and filtering on the document tree structure."""
447409
user = self.request.user
@@ -477,8 +439,9 @@ def get_queryset(self):
477439
def filter_queryset(self, queryset):
478440
"""Override to apply annotations to generic views."""
479441
queryset = super().filter_queryset(queryset)
480-
queryset = self.annotate_is_favorite(queryset)
481-
queryset = self.annotate_user_roles(queryset)
442+
user = self.request.user
443+
queryset = queryset.annotate_is_favorite(user)
444+
queryset = queryset.annotate_user_roles(user)
482445
return queryset
483446

484447
def get_response_for_queryset(self, queryset):
@@ -502,9 +465,10 @@ def list(self, request, *args, **kwargs):
502465
Additional annotations (e.g., `is_highest_ancestor_for_user`, favorite status) are
503466
applied before ordering and returning the response.
504467
"""
505-
queryset = (
506-
self.get_queryset()
507-
) # Not calling filter_queryset. We do our own cooking.
468+
user = self.request.user
469+
470+
# Not calling filter_queryset. We do our own cooking.
471+
queryset = self.get_queryset()
508472

509473
filterset = ListDocumentFilter(
510474
self.request.GET, queryset=queryset, request=self.request
@@ -517,7 +481,7 @@ def list(self, request, *args, **kwargs):
517481
for field in ["is_creator_me", "title"]:
518482
queryset = filterset.filters[field].filter(queryset, filter_data[field])
519483

520-
queryset = self.annotate_user_roles(queryset)
484+
queryset = queryset.annotate_user_roles(user)
521485

522486
# Among the results, we may have documents that are ancestors/descendants
523487
# of each other. In this case we want to keep only the highest ancestors.
@@ -534,7 +498,7 @@ def list(self, request, *args, **kwargs):
534498
)
535499

536500
# Annotate favorite status and filter if applicable as late as possible
537-
queryset = self.annotate_is_favorite(queryset)
501+
queryset = queryset.annotate_is_favorite(user)
538502
queryset = filterset.filters["is_favorite"].filter(
539503
queryset, filter_data["is_favorite"]
540504
)
@@ -625,7 +589,7 @@ def trashbin(self, request, *args, **kwargs):
625589
deleted_at__isnull=False,
626590
deleted_at__gte=models.get_trashbin_cutoff(),
627591
)
628-
queryset = self.annotate_user_roles(queryset)
592+
queryset = queryset.annotate_user_roles(self.request.user)
629593
queryset = queryset.filter(user_roles__contains=[models.RoleChoices.OWNER])
630594

631595
return self.get_response_for_queryset(queryset)
@@ -819,6 +783,8 @@ def tree(self, request, pk, *args, **kwargs):
819783
List ancestors tree above the document.
820784
What we need to display is the tree structure opened for the current document.
821785
"""
786+
user = self.request.user
787+
822788
try:
823789
current_document = self.queryset.only("depth", "path").get(pk=pk)
824790
except models.Document.DoesNotExist as excpt:
@@ -873,8 +839,8 @@ def tree(self, request, pk, *args, **kwargs):
873839
output_field=db.BooleanField(),
874840
)
875841
)
876-
queryset = self.annotate_user_roles(queryset)
877-
queryset = self.annotate_is_favorite(queryset)
842+
queryset = queryset.annotate_user_roles(user)
843+
queryset = queryset.annotate_is_favorite(user)
878844

879845
# Pass ancestors' links definitions to the serializer as a context variable
880846
# in order to allow saving time while computing abilities on the instance

src/backend/core/models.py

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -464,6 +464,41 @@ def readable_per_se(self, user):
464464

465465
return self.filter(link_reach=LinkReachChoices.PUBLIC)
466466

467+
def annotate_is_favorite(self, user):
468+
"""
469+
Annotate document queryset with the favorite status for the current user.
470+
"""
471+
if user.is_authenticated:
472+
favorite_exists_subquery = DocumentFavorite.objects.filter(
473+
document_id=models.OuterRef("pk"), user=user
474+
)
475+
return self.annotate(is_favorite=models.Exists(favorite_exists_subquery))
476+
477+
return self.annotate(is_favorite=models.Value(False))
478+
479+
def annotate_user_roles(self, user):
480+
"""
481+
Annotate document queryset with the roles of the current user
482+
on the document or its ancestors.
483+
"""
484+
output_field = ArrayField(base_field=models.CharField())
485+
486+
if user.is_authenticated:
487+
user_roles_subquery = DocumentAccess.objects.filter(
488+
models.Q(user=user) | models.Q(team__in=user.teams),
489+
document__path=Left(models.OuterRef("path"), Length("document__path")),
490+
).values_list("role", flat=True)
491+
492+
return self.annotate(
493+
user_roles=models.Func(
494+
user_roles_subquery, function="ARRAY", output_field=output_field
495+
)
496+
)
497+
498+
return self.annotate(
499+
user_roles=models.Value([], output_field=output_field),
500+
)
501+
467502

468503
class DocumentManager(MP_NodeManager.from_queryset(DocumentQuerySet)):
469504
"""

0 commit comments

Comments
 (0)