Coverage for apis_core/generic/api_views.py: 46%
37 statements
« prev ^ index » next coverage.py v7.5.3, created at 2026-01-07 08:21 +0000
« prev ^ index » next coverage.py v7.5.3, created at 2026-01-07 08:21 +0000
1from rest_framework import pagination, viewsets
3from apis_core.generic.schema import GenericAutoSchema
5from .filterbackends import GenericFilterBackend
6from .helpers import first_member_match, makeclassprefix, module_paths
7from .serializers import GenericHyperlinkedModelSerializer, serializer_factory
10class InjectFacetsLimitOffsetPagination(pagination.LimitOffsetPagination):
11 """
12 This pagination class injects a `facets` dict into the API response object.
13 It looks for a `get_facets` method in the models class and if this
14 method exists it uses its return value as the value of the `facets` key in
15 the API response.
16 To make the API response always paginated, we have to set the `default_limit`
17 attribute to something other than None.
18 """
20 default_limit = 20
21 facets = None
23 def paginate_queryset(self, queryset, request, view=None):
24 if hasattr(queryset.model, "get_facets"):
25 self.facets = queryset.model.get_facets(queryset)
26 return super().paginate_queryset(queryset, request, view)
28 def get_paginated_response(self, data):
29 response = super().get_paginated_response(data)
30 if self.facets is not None:
31 response.data.update({"facets": self.facets})
32 response.data = dict(sorted(response.data.items()))
33 return response
36class ModelViewSet(viewsets.ModelViewSet):
37 """
38 API ViewSet for a generic model.
39 The queryset is overridden by the first match from
40 the `first_member_match` helper.
41 The serializer class is overridden by the first match from
42 the `first_member_match` helper.
43 """
45 filter_backends = [GenericFilterBackend]
46 schema = GenericAutoSchema()
47 pagination_class = InjectFacetsLimitOffsetPagination
49 def dispatch(self, *args, **kwargs):
50 self.model = kwargs.get("contenttype").model_class()
51 return super().dispatch(*args, **kwargs)
53 def get_queryset(self):
54 queryset_methods = module_paths(
55 self.model, path="querysets", suffix="ViewSetQueryset"
56 )
57 queryset = first_member_match(queryset_methods) or (lambda x: x)
58 return queryset(self.model.objects.all())
60 def get_serializer_class(self):
61 renderer = getattr(getattr(self, "request", {}), "accepted_renderer", None)
62 serializer_class_modules = module_paths(
63 self.model, path="serializers", suffix="Serializer"
64 )
65 if renderer is not None:
66 prefix = makeclassprefix(renderer.format)
67 serializer_class_modules = (
68 module_paths(
69 self.model, path="serializers", suffix=f"{prefix}Serializer"
70 )
71 + serializer_class_modules
72 )
74 serializer_class = first_member_match(
75 serializer_class_modules,
76 getattr(renderer, "serializer", GenericHyperlinkedModelSerializer),
77 )
78 return serializer_factory(
79 self.model, serializer=serializer_class, action=self.action
80 )