Remove TwoModePagination; override paginator() on NetBoxModelViewSet to determine paginator class

This commit is contained in:
jeremystretch 2022-10-26 16:39:51 -04:00
parent fd1c8aa44a
commit a254398cec
3 changed files with 27 additions and 61 deletions

View File

@ -1,11 +1,13 @@
from django.db.models import QuerySet from django.db.models import QuerySet
from django.utils.encoding import force_str from rest_framework.pagination import LimitOffsetPagination, CursorPagination, _reverse_ordering
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 netbox.config import get_config from netbox.config import get_config
from rest_framework.compat import coreapi, coreschema
__all__ = (
'CursorPaginationWithNoLimit',
'OptionalLimitOffsetPagination',
'PAGINATORS',
)
class OptionalLimitOffsetPagination(LimitOffsetPagination): class OptionalLimitOffsetPagination(LimitOffsetPagination):
@ -183,58 +185,7 @@ class CursorPaginationWithNoLimit(CursorPagination):
return self.default_page_size return self.default_page_size
class TwoModePagination(BasePagination): PAGINATORS = {
""" 'limit_offset': OptionalLimitOffsetPagination, # Default per settings.DEFAULT_PAGINATION_CLASS
Pagination that allows user to toggle between LimitOffsetPagination and CursorPagination. The default 'cursor': CursorPaginationWithNoLimit,
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)

View File

@ -10,6 +10,7 @@ from rest_framework.viewsets import ModelViewSet
from extras.models import ExportTemplate from extras.models import ExportTemplate
from netbox.api.exceptions import SerializerNotFound from netbox.api.exceptions import SerializerNotFound
from netbox.api.pagination import PAGINATORS
from netbox.constants import NESTED_SERIALIZER_PREFIX from netbox.constants import NESTED_SERIALIZER_PREFIX
from utilities.api import get_serializer_for_model from utilities.api import get_serializer_for_model
from utilities.exceptions import AbortRequest from utilities.exceptions import AbortRequest
@ -93,6 +94,20 @@ class NetBoxModelViewSet(BulkUpdateModelMixin, BulkDestroyModelMixin, ObjectVali
return super().get_queryset() 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): def initialize_request(self, request, *args, **kwargs):
# Check if brief=True has been passed # Check if brief=True has been passed
if request.method == 'GET' and request.GET.get('brief'): if request.method == 'GET' and request.GET.get('brief'):

View File

@ -530,7 +530,7 @@ REST_FRAMEWORK = {
'rest_framework.filters.OrderingFilter', 'rest_framework.filters.OrderingFilter',
), ),
'DEFAULT_METADATA_CLASS': 'netbox.api.metadata.BulkOperationMetadata', 'DEFAULT_METADATA_CLASS': 'netbox.api.metadata.BulkOperationMetadata',
'DEFAULT_PAGINATION_CLASS': 'netbox.api.pagination.TwoModePagination', 'DEFAULT_PAGINATION_CLASS': 'netbox.api.pagination.OptionalLimitOffsetPagination',
'DEFAULT_PARSER_CLASSES': ( 'DEFAULT_PARSER_CLASSES': (
'rest_framework.parsers.JSONParser', 'rest_framework.parsers.JSONParser',
'rest_framework.parsers.MultiPartParser', 'rest_framework.parsers.MultiPartParser',