drf-yasg icon indicating copy to clipboard operation
drf-yasg copied to clipboard

Question: pagination on APIView(Post)

Open mke21 opened this issue 4 years ago • 4 comments

Hi,

Is it possible to do pagination on a post in an APIView? I don't seem to understand how that could be done. I've got the following:

class LargeResultPagination(PageNumberPagination):
    """
    Paginator for all apis
    """
    page_size = 100000
    max_page_size = 100000
    page_size_query_param = 'page_size'

class Data(APIView):
    """
    Demo of my view
    """
    pagination_class = LargeResultPagination

    @swagger_auto_schema(
        operation_description='...,
        request_body=BodySerializer,
        responses={200: ReturnSerializer(
             many=True
        )},
        pagination_class=LargeResultPagination,
        paginator_inspectors=[PaginatorInspector]
    )
    def post(self, request, version, format=None):
         ...

But the swagger page doesn't show any pagination in the responses model. What am I understanding wrong?

mke21 avatar Apr 14 '20 10:04 mke21

I also need answer for this problem, in the documents mentioned that paginator_inspectors is the only way to add pagination to the response example, but this case it doesn't seem to work with APIView(even on GET request).

linhnvhicts avatar Nov 12 '20 04:11 linhnvhicts

I have the same problem - in my case i have custom serializer in list-action in my ModelViewSet, that is different from the serializer_class. I use swagger_auto_schema same way

class SomeViewSet(viewsets.ModelViewSet):
    serializer_class = SomeSerializer

    ...

    @swagger_auto_schema(responses={200: SomeCustomSerializer(many=True)})
    def list(self, request):
        ...

But in generated swagger docs i see just a list of data from SomeCustomSerialaizer, not wrapped in DEFAULT_PAGINATION_CLASS: rest_framework.pagination.PageNumberPagination. I also use the default DEFAULT_PAGINATOR_INSPECTORS settings.

It seems when i specify responses, drf-yasg ignores default pagination settings. Also explicit indication on pginator inspectors in swagger_auto_schema does not help.

tuenut avatar May 25 '21 10:05 tuenut

I have sort of fix for that situation.

https://github.com/tuenut/drf-yasg/commit/d20c44afd6674cd3e66ea350564e6c08e1f16cc1

But i have not tested that solution and can say what ripple it can produce. You can crea your own subclass of SwaggerAutoSchema and override that method, and then use that subclass like as

@swagger_auto_schema(
    auto_schema=YourOwnSubclassOfSwaggerAutoSchema, 
    responses={200: SomeCustomSerializer(many=True)}
)
def list(self, request):
    ...

tuenut avatar May 25 '21 13:05 tuenut

My solution for this. Only for APIView. Works with any methods of APIView: GET, POST, PATCH, PUT, DELETE.

from drf_yasg import openapi
from drf_yasg.utils import guess_response_status
from drf_yasg.inspectors import SwaggerAutoSchema


class CustomSwaggerAutoSchema(SwaggerAutoSchema):

    def get_responses(self):
        response_serializers = self.get_response_serializers()
        response_schemas = self.get_response_schemas(response_serializers)

        paginator = self.overrides.get('paginator', None)
        if paginator and self.has_list_response():
            method = self.method.lower()
            default_response_status = str(guess_response_status(method))
            if default_response_status in response_schemas:
                response_schemas[default_response_status] = openapi.Response(
                    description=response_schemas[default_response_status].description,
                    schema=self.get_paginated_response(
                        response_schemas[default_response_status].schema
                    )
                )

        return openapi.Responses(responses=response_schemas)

    def get_paginated_response(self, response_schema):
        return self.probe_inspectors(self.paginator_inspectors, 'get_paginated_response',
                                     self._get_paginator(), response_schema=response_schema)

    def get_pagination_parameters(self):
        if not self.should_page():
            return []

        return self.probe_inspectors(self.paginator_inspectors, 'get_paginator_parameters',
                                     self._get_paginator()) or []

    def should_page(self):
        return self._get_paginator() and self.has_list_response()

    def _get_paginator(self):
        return self.overrides.get('paginator') or getattr(self.view, 'paginator', None)

In settings:

SWAGGER_SETTINGS = {
    ...
    'DEFAULT_AUTO_SCHEMA_CLASS': 'website_apps.api.utils.swagger.CustomSwaggerAutoSchema',
}

In view code:

class EntityListView(APIView):
    class ListPagination(pagination.CursorPagination):
        page_size = 20    

    @swagger_auto_schema(
        operation_description=_('Returns entity list with pagination'),
        query_serializer=EntityListQueryFilter(),
        responses={
            200: openapi.Response(
                description=_('Paginated entity list'),
                schema=EntityOutputSerializer(many=True)
            )
        },
        paginator=ListPagination()
    )
    def get(self, request):
        serializer = serializers.EntityListQueryFilter(data=request.query_params)
        serializer.is_valid(raise_exception=True)

        entities = get_entities(**serializer.validated_data)
        paginator = self.ListPagination()
        entities = paginator.paginate_queryset(entities, request)

        serializer = EntityOutputSerializer(entities, many=True)
        return paginator.get_paginated_response(serializer.data)

2mitrij avatar Jul 04 '21 09:07 2mitrij