From a254398cecbb23939caa8684445aaed586f9e0c9 Mon Sep 17 00:00:00 2001 From: jeremystretch Date: Wed, 26 Oct 2022 16:39:51 -0400 Subject: [PATCH] Remove TwoModePagination; override paginator() on NetBoxModelViewSet to determine paginator class --- netbox/netbox/api/pagination.py | 71 ++++---------------------- netbox/netbox/api/viewsets/__init__.py | 15 ++++++ netbox/netbox/settings.py | 2 +- 3 files changed, 27 insertions(+), 61 deletions(-) diff --git a/netbox/netbox/api/pagination.py b/netbox/netbox/api/pagination.py index cd443a725..e8edbbd2e 100644 --- a/netbox/netbox/api/pagination.py +++ b/netbox/netbox/api/pagination.py @@ -1,11 +1,13 @@ from django.db.models import QuerySet -from django.utils.encoding import force_str -from django.utils.translation import gettext_lazy as _ -from rest_framework.pagination import LimitOffsetPagination, CursorPagination, BasePagination, _reverse_ordering -from rest_framework.response import Response +from rest_framework.pagination import LimitOffsetPagination, CursorPagination, _reverse_ordering from netbox.config import get_config -from rest_framework.compat import coreapi, coreschema + +__all__ = ( + 'CursorPaginationWithNoLimit', + 'OptionalLimitOffsetPagination', + 'PAGINATORS', +) class OptionalLimitOffsetPagination(LimitOffsetPagination): @@ -183,58 +185,7 @@ class CursorPaginationWithNoLimit(CursorPagination): return self.default_page_size -class TwoModePagination(BasePagination): - """ - Pagination that allows user to toggle between LimitOffsetPagination and CursorPagination. The default - is LimitOffsetPagination. - """ - pagination_mode_param = 'pagination_mode' - pagination_mode_description = _( - 'Mode selector for LimitOffsetPagination (default) and CursorPagination.\n' - '`offset` and `limit` are used for LimitOffsetPagination.\n' - '`cursor` and `limit` are used for CursorPagination.') - limit_offset_key = 'limit_offset' - cursor_pagination_key = 'cursor' - - def __init__( - self, - limit_offset_paginator=OptionalLimitOffsetPagination, - cursor_paginator=CursorPaginationWithNoLimit, - ) -> None: - self._limit_offset_pagination = limit_offset_paginator() - self._cursor_pagination = cursor_paginator() - - self._chosen_pagination = self._limit_offset_pagination - - def paginate_queryset(self, queryset, request: Response, view=None): - mode = request.query_params.get(self.pagination_mode_param) - if mode == self.cursor_pagination_key: - self._chosen_pagination = self._cursor_pagination - return self._chosen_pagination.paginate_queryset(queryset, request, view) - - def get_paginated_response(self, data): - return self._chosen_pagination.get_paginated_response(data) - - def get_paginated_response_schema(self, schema): - return self._chosen_pagination.get_paginated_response_schema(schema) - - def to_html(self): # pragma: no cover - return self._chosen_pagination.to_html() - - def get_schema_fields(self, view): - mode_field = coreapi.Field( - name=self.pagination_mode_param, - required=False, - location='query', - schema=coreschema.Enum( - title='Pagination Mode', - description=force_str(self.pagination_mode_description), - enum=[self.limit_offset_key, self.cursor_pagination_key], - ) - ) - return [mode_field] \ - + self._limit_offset_pagination.get_schema_fields(view) \ - + self._cursor_pagination.get_schema_fields(view)[:1] # "limit" is shared between the two modes - - def get_schema_operation_parameters(self, view): - return self._chosen_pagination.get_schema_operation_parameters(view) +PAGINATORS = { + 'limit_offset': OptionalLimitOffsetPagination, # Default per settings.DEFAULT_PAGINATION_CLASS + 'cursor': CursorPaginationWithNoLimit, +} diff --git a/netbox/netbox/api/viewsets/__init__.py b/netbox/netbox/api/viewsets/__init__.py index c50ad9ca6..bed2dfbab 100644 --- a/netbox/netbox/api/viewsets/__init__.py +++ b/netbox/netbox/api/viewsets/__init__.py @@ -10,6 +10,7 @@ from rest_framework.viewsets import ModelViewSet from extras.models import ExportTemplate from netbox.api.exceptions import SerializerNotFound +from netbox.api.pagination import PAGINATORS from netbox.constants import NESTED_SERIALIZER_PREFIX from utilities.api import get_serializer_for_model from utilities.exceptions import AbortRequest @@ -93,6 +94,20 @@ class NetBoxModelViewSet(BulkUpdateModelMixin, BulkDestroyModelMixin, ObjectVali return super().get_queryset() + @property + def paginator(self): + """ + Allow the request to designate the paginator class per the pagination_mode parameter. + """ + if not hasattr(self, '_paginator'): + if self.pagination_class is None: + self._paginator = None + elif mode := self.request.query_params.get('pagination_mode'): + self._paginator = PAGINATORS.get(mode, self.pagination_class)() + else: + self._paginator = self.pagination_class() + return self._paginator + def initialize_request(self, request, *args, **kwargs): # Check if brief=True has been passed if request.method == 'GET' and request.GET.get('brief'): diff --git a/netbox/netbox/settings.py b/netbox/netbox/settings.py index 2507ed974..e1fe10a69 100644 --- a/netbox/netbox/settings.py +++ b/netbox/netbox/settings.py @@ -530,7 +530,7 @@ REST_FRAMEWORK = { 'rest_framework.filters.OrderingFilter', ), 'DEFAULT_METADATA_CLASS': 'netbox.api.metadata.BulkOperationMetadata', - 'DEFAULT_PAGINATION_CLASS': 'netbox.api.pagination.TwoModePagination', + 'DEFAULT_PAGINATION_CLASS': 'netbox.api.pagination.OptionalLimitOffsetPagination', 'DEFAULT_PARSER_CLASSES': ( 'rest_framework.parsers.JSONParser', 'rest_framework.parsers.MultiPartParser',