'Django PageNumberPagination customize error if page number out of range

I'm currently trying to create an API that return list of objects with page and limit per page input from url parameter using django-rest-framework which i already done in my api view with custom Pagination

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)

Currently if a page is out of range( for example if i have only 2 object and i set my url to page=3 and page_size=1 then it should out of range of total objects) then in the response it will return a 404 status and in the body:

{
    "detail": "Invalid page."
}

Is there a way to customize for it to return 400 status and the following json body ?

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

Thank you



Solution 1:[1]

You can achieve it by overriding NotFound class and thenpaginate_queryset method,

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)

Solution 2:[2]

You can simply, wrap the pagination code in try-except, this is just a temporary solution I found.

Expect block will be executed instead of {"detail": "Invalid page."} message

try :
    context = paginator.paginate_queryset(filteredData, request)
    serializer = AuthUserSerializer(context,many=True)
    return Response(serializer.data)
except:
    # return here any message you want
    return Response({
        "code": 400,
        "error": "Page out of range"
    })

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1
Solution 2 Rushi Creates