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

1from rest_framework import pagination, viewsets 

2 

3from apis_core.generic.schema import GenericAutoSchema 

4 

5from .filterbackends import GenericFilterBackend 

6from .helpers import first_member_match, makeclassprefix, module_paths 

7from .serializers import GenericHyperlinkedModelSerializer, serializer_factory 

8 

9 

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 """ 

19 

20 default_limit = 20 

21 facets = None 

22 

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) 

27 

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 

34 

35 

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 """ 

44 

45 filter_backends = [GenericFilterBackend] 

46 schema = GenericAutoSchema() 

47 pagination_class = InjectFacetsLimitOffsetPagination 

48 

49 def dispatch(self, *args, **kwargs): 

50 self.model = kwargs.get("contenttype").model_class() 

51 return super().dispatch(*args, **kwargs) 

52 

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()) 

59 

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 ) 

73 

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 )