Skip to content

Commit 64c312b

Browse files
committed
♻️(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 ddc622f commit 64c312b

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
@@ -400,44 +400,6 @@ class DocumentViewSet(
400400
trashbin_serializer_class = serializers.ListDocumentSerializer
401401
tree_serializer_class = serializers.ListDocumentSerializer
402402

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

480443
def get_response_for_queryset(self, queryset):
@@ -498,9 +461,10 @@ def list(self, request, *args, **kwargs):
498461
Additional annotations (e.g., `is_highest_ancestor_for_user`, favorite status) are
499462
applied before ordering and returning the response.
500463
"""
501-
queryset = (
502-
self.get_queryset()
503-
) # Not calling filter_queryset. We do our own cooking.
464+
user = self.request.user
465+
466+
# Not calling filter_queryset. We do our own cooking.
467+
queryset = self.get_queryset()
504468

505469
filterset = ListDocumentFilter(
506470
self.request.GET, queryset=queryset, request=self.request
@@ -513,7 +477,7 @@ def list(self, request, *args, **kwargs):
513477
for field in ["is_creator_me", "title"]:
514478
queryset = filterset.filters[field].filter(queryset, filter_data[field])
515479

516-
queryset = self.annotate_user_roles(queryset)
480+
queryset = queryset.annotate_user_roles(user)
517481

518482
# Among the results, we may have documents that are ancestors/descendants
519483
# of each other. In this case we want to keep only the highest ancestors.
@@ -530,7 +494,7 @@ def list(self, request, *args, **kwargs):
530494
)
531495

532496
# Annotate favorite status and filter if applicable as late as possible
533-
queryset = self.annotate_is_favorite(queryset)
497+
queryset = queryset.annotate_is_favorite(user)
534498
queryset = filterset.filters["is_favorite"].filter(
535499
queryset, filter_data["is_favorite"]
536500
)
@@ -613,7 +577,7 @@ def trashbin(self, request, *args, **kwargs):
613577
deleted_at__isnull=False,
614578
deleted_at__gte=models.get_trashbin_cutoff(),
615579
)
616-
queryset = self.annotate_user_roles(queryset)
580+
queryset = queryset.annotate_user_roles(self.request.user)
617581
queryset = queryset.filter(user_roles__contains=[models.RoleChoices.OWNER])
618582

619583
return self.get_response_for_queryset(queryset)
@@ -793,6 +757,8 @@ def tree(self, request, pk, *args, **kwargs):
793757
List ancestors tree above the document.
794758
What we need to display is the tree structure opened for the current document.
795759
"""
760+
user = self.request.user
761+
796762
try:
797763
current_document = self.queryset.only("depth", "path").get(pk=pk)
798764
except models.Document.DoesNotExist as excpt:
@@ -847,8 +813,8 @@ def tree(self, request, pk, *args, **kwargs):
847813
output_field=db.BooleanField(),
848814
)
849815
)
850-
queryset = self.annotate_user_roles(queryset)
851-
queryset = self.annotate_is_favorite(queryset)
816+
queryset = queryset.annotate_user_roles(user)
817+
queryset = queryset.annotate_is_favorite(user)
852818

853819
# Pass ancestors' links definitions to the serializer as a context variable
854820
# 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)