(backend) add new "descendants" action to document API endpoint

We want to be able to make a search query inside a hierchical document.
It's elegant to do it as a document detail action so that we benefit
from access control.
This commit is contained in:
Samuel Paccoud - DINUM
2025-02-17 10:25:07 +01:00
committed by Manuel Raynaud
parent 56aa69f56a
commit 2203d49a52
10 changed files with 824 additions and 23 deletions

View File

@@ -177,7 +177,14 @@ class ListDocumentSerializer(serializers.ModelSerializer):
request = self.context.get("request")
if request:
return document.get_abilities(request.user)
paths_links_mapping = self.context.get("paths_links_mapping", None)
# Retrieve ancestor links from paths_links_mapping (if provided)
ancestors_links = (
paths_links_mapping.get(document.path[: -document.steplen])
if paths_links_mapping
else None
)
return document.get_abilities(request.user, ancestors_links=ancestors_links)
return {}

View File

@@ -4,7 +4,6 @@
import logging
import re
import uuid
from collections import defaultdict
from urllib.parse import urlparse
from django.conf import settings
@@ -21,7 +20,6 @@ from django.http import Http404
import rest_framework as drf
from botocore.exceptions import ClientError
from django_filters import rest_framework as drf_filters
from rest_framework import filters, status, viewsets
from rest_framework import response as drf_response
from rest_framework.permissions import AllowAny
@@ -424,6 +422,7 @@ class DocumentViewSet(
serializer_class = serializers.DocumentSerializer
ai_translate_serializer_class = serializers.AITranslateSerializer
children_serializer_class = serializers.ListDocumentSerializer
descendants_serializer_class = serializers.ListDocumentSerializer
list_serializer_class = serializers.ListDocumentSerializer
trashbin_serializer_class = serializers.ListDocumentSerializer
tree_serializer_class = serializers.ListDocumentSerializer
@@ -841,17 +840,24 @@ class DocumentViewSet(
else drf.exceptions.NotAuthenticated()
)
ancestors_links_definitions = defaultdict(set)
paths_links_mapping = {}
ancestors_links = []
children_clause = db.Q()
for ancestor in ancestors:
if ancestor.depth < highest_readable.depth:
continue
ancestors_links_definitions[ancestor.link_reach].add(ancestor.link_role)
children_clause |= db.Q(
path__startswith=ancestor.path, depth=ancestor.depth + 1
)
# Compute cache for ancestors links to avoid many queries while computing
# abilties for his documents in the tree!
ancestors_links.append(
{"link_reach": ancestor.link_reach, "link_role": ancestor.link_role}
)
paths_links_mapping[ancestor.path] = ancestors_links.copy()
children = self.queryset.filter(children_clause, deleted_at__isnull=True)
queryset = ancestors.filter(depth__gte=highest_readable.depth) | children
@@ -866,7 +872,7 @@ class DocumentViewSet(
many=True,
context={
"request": request,
"ancestors_links_definitions": ancestors_links_definitions,
"paths_links_mapping": paths_links_mapping,
},
)
return drf.response.Response(