首页 > 解决方案 > 如果页码超出范围,Django PageNumberPagination 自定义错误

问题描述

我目前正在尝试创建一个 API,它使用 django-rest-framework 从 url 参数返回带有页面的对象列表并限制每页输入,我已经在我的 api 视图中使用自定义分页完成了

class PropertyListPagination(PageNumberPagination):
    page_size = 20
    page_size_query_param = 'page_size'

    def get_paginated_response(self, data):
        return Response({
            'code': 200,
            'data': data
        })

@api_view(['GET'])
def property_list(request):
    if request.method == 'GET':
        paginator = PropertyListPagination()
        queryset = Property.objects.all()
        context = paginator.paginate_queryset(queryset, request)
        serializer = PropertySerializer(context, many=True)
        return paginator.get_paginated_response(serializer.data)

目前,如果页面超出范围(例如,如果我只有 2 个对象并且我将我的 url 设置为 page=3 和 page_size=1 那么它应该超出对象总数的范围)然后在响应中它将返回 404 状态在体内:

{
    "detail": "Invalid page."
}

有没有办法自定义它以返回 400 状态和以下 json body ?

{
    "code": 400,
    "error": "Page out of range"
}

谢谢

标签: pythondjangodjango-rest-frameworkcustomizationdjango-pagination

解决方案


您可以通过覆盖NotFound类然后paginate_queryset方法来实现它,

from rest_framework.exceptions import NotFound  
from rest_framework.exceptions import APIException

class NotFound(APIException):
    status_code = status.HTTP_400_BAD_REQUEST
    default_detail = ('bad_request.')
    default_code = 'bad_request'

class PropertyListPagination(PageNumberPagination):
    page_size = 20
    page_size_query_param = 'page_size'

    def paginate_queryset(self, queryset, request, view=None):
        """
        Paginate a queryset if required, either returning a
        page object, or `None` if pagination is not configured for this view.
        """
        page_size = self.get_page_size(request)
        if not page_size:
            return None

        paginator = self.django_paginator_class(queryset, page_size)
        page_number = request.query_params.get(self.page_query_param, 1)
        if page_number in self.last_page_strings:
            page_number = paginator.num_pages

        try:
            self.page = paginator.page(page_number)
        except Exception as exc:
            # Here it is
            msg = {
                "code": 400 # you can remove this line as now the status code will be 400 by default as we have override it in `NotFound` class(see above)
                "error": "Page out of range"
            }
            raise NotFound(msg)

        if paginator.num_pages > 1 and self.template is not None:
            # The browsable API should display pagination controls.
            self.display_page_controls = True

        self.request = request
        return list(self.page)

推荐阅读