覆盖Django Rest Framework中的CursorPagination的page_size和顺序

时间:2019-01-15 11:54:28

标签: django django-rest-framework

我使用Django Rest Framework的CursorPagination,我想在单个视图集中覆盖默认的page_size(10)ordering('timestamp')。我该怎么办?

我尝试过使用视图集,但未成功:

from rest_framework.pagination import CursorPagination
class ListAPIView(ListAPIView):
    queryset = Cake.objects.all()
    permission_classes = [AllowAny]
    serializer_class = ListSerializer
    pagination_class = CursorPagination
    filter_backends = (OrderingFilter, DjangoFilterBackend)
    filter_class = CakeListFilter
    filterset_fields = ('cake_type', 'user__username')
    ordering = '-date'
    page_size = 5

3 个答案:

答案 0 :(得分:1)

您可以创建一个继承自CursorPagination类的新类,以便像这样设置自定义page_size和/或max_page_size

class CustomPageSizeCursorPagination(CursorPagination):
    page_size = 5
    max_page_size = 100

然后将此类用作您的视图集的pagination_class字段

警告:以下代码未经测试

另一个选择是编写一个自定义分页器类,该类从视图集中获取页面大小。例如:

class PageSizeInViewSetCursorPagination(CursorPagination):
    def get_page_size(self, request, viewset_page_size):
        if self.page_size_query_param:
            try:
                return _positive_int(
                    request.query_params[self.page_size_query_param],
                    strict=True,
                    cutoff=self.max_page_size
                )
            except (KeyError, ValueError):
                pass

        return viewset_page_size or self.page_size

    def paginate_queryset(self, queryset, request, view=None):
        # Get the page_size from the viewset and then decide which page_size to use
        viewset_page_size = getattr(view, 'page_size', None) 
        page_size = self.get_page_size(request, viewset_page_size)


        # What follows is copy/paste of the code from CursorPagination paginate_queryset method
        if not self.page_size:
            return None

        self.base_url = request.build_absolute_uri()
        self.ordering = self.get_ordering(request, queryset, view)

        self.cursor = self.decode_cursor(request)
        if self.cursor is None:
            (offset, reverse, current_position) = (0, False, None)
        else:
            (offset, reverse, current_position) = self.cursor

        # Cursor pagination always enforces an ordering.
        if reverse:
            queryset = queryset.order_by(*_reverse_ordering(self.ordering))
        else:
            queryset = queryset.order_by(*self.ordering)

        # If we have a cursor with a fixed position then filter by that.
        if current_position is not None:
            order = self.ordering[0]
            is_reversed = order.startswith('-')
            order_attr = order.lstrip('-')

            # Test for: (cursor reversed) XOR (queryset reversed)
            if self.cursor.reverse != is_reversed:
                kwargs = {order_attr + '__lt': current_position}
            else:
                kwargs = {order_attr + '__gt': current_position}

            queryset = queryset.filter(**kwargs)

        # If we have an offset cursor then offset the entire page by that amount.
        # We also always fetch an extra item in order to determine if there is a
        # page following on from this one.
        results = list(queryset[offset:offset + self.page_size + 1])
        self.page = list(results[:self.page_size])

        # Determine the position of the final item following the page.
        if len(results) > len(self.page):
            has_following_position = True
            following_position = self._get_position_from_instance(results[-1], self.ordering)
        else:
            has_following_position = False
            following_position = None

        # If we have a reverse queryset, then the query ordering was in reverse
        # so we need to reverse the items again before returning them to the user.
        if reverse:
            self.page = list(reversed(self.page))

        if reverse:
            # Determine next and previous positions for reverse cursors.
            self.has_next = (current_position is not None) or (offset > 0)
            self.has_previous = has_following_position
            if self.has_next:
                self.next_position = current_position
            if self.has_previous:
                self.previous_position = following_position
        else:
            # Determine next and previous positions for forward cursors.
            self.has_next = has_following_position
            self.has_previous = (current_position is not None) or (offset > 0)
            if self.has_next:
                self.next_position = following_position
            if self.has_previous:
                self.previous_position = current_position

        # Display page controls in the browsable API if there is more
        # than one page.
        if (self.has_previous or self.has_next) and self.template is not None:
            self.display_page_controls = True

        return self.page

请注意,在以上示例中,page_size中的request始终优先于您在代码中设置的内容。然后viewset_page_size是第二行,最后是page_size类中的聋人Pagination

答案 1 :(得分:1)

这是自定义的分页类,扩展了CursorPagination。它检查在视图集中定义的ordering和page_size属性,如果存在则使用它们。如果不是,则退回到分页类本身中定义的原始设置。

class NewsCursorPaginator(CursorPagination):
    ordering = 'title'
    page_size = 5

    # get_page_size do not have view attribute, so we have our custom one
    def get_custom_page_size(self, request, view):
        viewset_page_size = getattr(view, 'page_size', None)
        if viewset_page_size:
            self.page_size = viewset_page_size
        return super(NewsCursorPaginator, self).get_page_size(request)

    def get_ordering(self, request, queryset, view):
        viewset_ordering = getattr(view, 'ordering', None)
        if viewset_ordering:
            self.ordering = viewset_ordering
        return super(NewsCursorPaginator, self).get_ordering(request, queryset, view)

    def paginate_queryset(self, queryset, request, view=None):
        self.page_size = self.get_custom_page_size(request, view)
        return super(NewsCursorPaginator, self).paginate_queryset(queryset, request, view)

答案 2 :(得分:0)

此实现将“ limit”(page_size)作为可选的querystring参数。

class CursorPagination(pagination.CursorPagination):
    page_size = settings.REST_FRAMEWORK["PAGE_SIZE"]

    def get_custom_page_size(self, request, view):
        try:
            self.page_size = int(request.GET.get("limit"))
        except (ValueError, TypeError):
            pass
        return super().get_page_size(request)

    def paginate_queryset(self, queryset, request, view=None):
        self.page_size = self.get_custom_page_size(request, view)
        return super().paginate_queryset(queryset, request, view)