143 lines
4.5 KiB
Python
143 lines
4.5 KiB
Python
|
|
"""API endpoints"""
|
||
|
|
from rest_framework import (
|
||
|
|
decorators,
|
||
|
|
mixins,
|
||
|
|
pagination,
|
||
|
|
viewsets,
|
||
|
|
)
|
||
|
|
from rest_framework import (
|
||
|
|
response as drf_response,
|
||
|
|
)
|
||
|
|
|
||
|
|
from core import models
|
||
|
|
|
||
|
|
from . import permissions, serializers
|
||
|
|
|
||
|
|
# pylint: disable=too-many-ancestors
|
||
|
|
|
||
|
|
|
||
|
|
class NestedGenericViewSet(viewsets.GenericViewSet):
|
||
|
|
"""
|
||
|
|
A generic Viewset aims to be used in a nested route context.
|
||
|
|
e.g: `/api/v1.0/resource_1/<resource_1_pk>/resource_2/<resource_2_pk>/`
|
||
|
|
|
||
|
|
It allows to define all url kwargs and lookup fields to perform the lookup.
|
||
|
|
"""
|
||
|
|
|
||
|
|
lookup_fields: list[str] = ["pk"]
|
||
|
|
lookup_url_kwargs: list[str] = []
|
||
|
|
|
||
|
|
def __getattribute__(self, item):
|
||
|
|
"""
|
||
|
|
This method is overridden to allow to get the last lookup field or lookup url kwarg
|
||
|
|
when accessing the `lookup_field` or `lookup_url_kwarg` attribute. This is useful
|
||
|
|
to keep compatibility with all methods used by the parent class `GenericViewSet`.
|
||
|
|
"""
|
||
|
|
if item in ["lookup_field", "lookup_url_kwarg"]:
|
||
|
|
return getattr(self, item + "s", [None])[-1]
|
||
|
|
|
||
|
|
return super().__getattribute__(item)
|
||
|
|
|
||
|
|
def get_queryset(self):
|
||
|
|
"""
|
||
|
|
Get the list of items for this view.
|
||
|
|
|
||
|
|
`lookup_fields` attribute is enumerated here to perform the nested lookup.
|
||
|
|
"""
|
||
|
|
queryset = super().get_queryset()
|
||
|
|
|
||
|
|
# The last lookup field is removed to perform the nested lookup as it corresponds
|
||
|
|
# to the object pk, it is used within get_object method.
|
||
|
|
lookup_url_kwargs = (
|
||
|
|
self.lookup_url_kwargs[:-1]
|
||
|
|
if self.lookup_url_kwargs
|
||
|
|
else self.lookup_fields[:-1]
|
||
|
|
)
|
||
|
|
|
||
|
|
filter_kwargs = {}
|
||
|
|
for index, lookup_url_kwarg in enumerate(lookup_url_kwargs):
|
||
|
|
if lookup_url_kwarg not in self.kwargs:
|
||
|
|
raise KeyError(
|
||
|
|
f"Expected view {self.__class__.__name__} to be called with a URL "
|
||
|
|
f'keyword argument named "{lookup_url_kwarg}". Fix your URL conf, or '
|
||
|
|
"set the `.lookup_fields` attribute on the view correctly."
|
||
|
|
)
|
||
|
|
|
||
|
|
filter_kwargs.update(
|
||
|
|
{self.lookup_fields[index]: self.kwargs[lookup_url_kwarg]}
|
||
|
|
)
|
||
|
|
|
||
|
|
return queryset.filter(**filter_kwargs)
|
||
|
|
|
||
|
|
|
||
|
|
class SerializerPerActionMixin:
|
||
|
|
"""
|
||
|
|
A mixin to allow to define serializer classes for each action.
|
||
|
|
|
||
|
|
This mixin is useful to avoid to define a serializer class for each action in the
|
||
|
|
`get_serializer_class` method.
|
||
|
|
"""
|
||
|
|
|
||
|
|
serializer_classes: dict[str, type] = {}
|
||
|
|
default_serializer_class: type = None
|
||
|
|
|
||
|
|
def get_serializer_class(self):
|
||
|
|
"""
|
||
|
|
Return the serializer class to use depending on the action.
|
||
|
|
"""
|
||
|
|
return self.serializer_classes.get(self.action, self.default_serializer_class)
|
||
|
|
|
||
|
|
|
||
|
|
class Pagination(pagination.PageNumberPagination):
|
||
|
|
"""Pagination to display no more than 100 objects per page sorted by creation date."""
|
||
|
|
|
||
|
|
ordering = "-created_on"
|
||
|
|
max_page_size = 100
|
||
|
|
page_size_query_param = "page_size"
|
||
|
|
|
||
|
|
|
||
|
|
class UserViewSet(
|
||
|
|
mixins.UpdateModelMixin, viewsets.GenericViewSet, mixins.ListModelMixin
|
||
|
|
):
|
||
|
|
"""User ViewSet"""
|
||
|
|
|
||
|
|
permission_classes = [permissions.IsSelf]
|
||
|
|
queryset = models.User.objects.all()
|
||
|
|
serializer_class = serializers.UserSerializer
|
||
|
|
|
||
|
|
def get_queryset(self):
|
||
|
|
"""
|
||
|
|
Limit listed users by querying the email field with a trigram similarity
|
||
|
|
search if a query is provided.
|
||
|
|
Limit listed users by excluding users already in the document if a document_id
|
||
|
|
is provided.
|
||
|
|
"""
|
||
|
|
queryset = self.queryset
|
||
|
|
|
||
|
|
if self.action == "list":
|
||
|
|
# Exclude all users already in the given document
|
||
|
|
if document_id := self.request.GET.get("document_id", ""):
|
||
|
|
queryset = queryset.exclude(documentaccess__document_id=document_id)
|
||
|
|
|
||
|
|
# Filter users by email similarity
|
||
|
|
if query := self.request.GET.get("q", ""):
|
||
|
|
queryset = queryset.filter(email__trigram_word_similar=query)
|
||
|
|
|
||
|
|
return queryset
|
||
|
|
|
||
|
|
@decorators.action(
|
||
|
|
detail=False,
|
||
|
|
methods=["get"],
|
||
|
|
url_name="me",
|
||
|
|
url_path="me",
|
||
|
|
permission_classes=[permissions.IsAuthenticated],
|
||
|
|
)
|
||
|
|
def get_me(self, request):
|
||
|
|
"""
|
||
|
|
Return information on currently logged user
|
||
|
|
"""
|
||
|
|
context = {"request": request}
|
||
|
|
return drf_response.Response(
|
||
|
|
self.serializer_class(request.user, context=context).data
|
||
|
|
)
|