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

[Bug] Custom Pagination Class with get_paginated_response_schema does not work when there are 2 view with same serializer

Open Atidhaya opened this issue 1 year ago • 2 comments

Describe the bug Hello! I wanted to use my custom pagination and override the schema. I've searched and found that such issue is already taken care of #1048

However, I was not be able to reproduce the same outcome When item in paginated response is similar to another paginated response. No longer what I do with get_paginated_response_schema. It does not change the out come of how my swagger is generated

To Reproduce You have 2 view using the 2 different paginated response with the same serializer class.

Have your second custom pagination class and override get_paginated_response_schema. I even try the extreme method in returning empy dictionary

The 1st pagination class

class CustomCursorPagination(CursorPagination):
    ordering = "-created_at"
    page_size = 20
    page_size_query_param = "page_size"

The view

class GeneralTradeOrderViewSet(viewsets.GenericViewSet, mixins.ListModelMixin, mixins.RetrieveModelMixin):
    permission_classes = (GeneralTradeOrderPolicy,)
    queryset = Order.objects.all()
    pagination_class = CustomCursorPagination
    filter_backends = [django_filters.DjangoFilterBackend, filters.SearchFilter]
    filterset_class = OrderFilterSet
    search_fields = ["id", "wholesale__name"]

    def get_serializer_class(
        self,
    ) -> Type[OrderListSerializer | OrderDetailGetSerializer]:
        if self.action == "list":
            return OrderListSerializer
        return OrderDetailGetSerializer

2nd Pagination and view

class MyCursorPagination(CursorPagination):
    ordering = "-created_at"
    page_size = 20
    page_size_query_param = "page_size"

    def get_paginated_response_schema(self, schema: Any) -> dict:
        print("IN MY PAGINATED RESPONSE")
        return {}

put your pagination class inside view

class WholesaleOrderViewSet(viewsets.ModelViewSet):
    permission_classes = (WholesaleOrderPolicy,)
    queryset = Order.objects.all()
    pagination_class = MyCursorPagination
    
    def get_serializer_class(
        self,
    ) -> Type[OrderListSerializer | OrderDetailGetSerializer | EditOrderRequestSerializer]:
        if self.action == "list":
            return OrderListSerializer
        elif self.action == "update":
            return EditOrderRequestSerializer
        return OrderDetailGetSerializer

As you can see, both class are using different pagination class but with the same serializer class.

then when you retrieve swagger. The 2nd class with empty dictionary response for schema still produces default response for pagination with correct serializer

Screenshot 2567-02-07 at 11 11 37

I use a debugging tool and found out that, somehow, in openapi.py

when it calls get_paginated_response the result schema is still correct order item

Screenshot 2567-02-07 at 11 37 07

function then call in openapi.py but schema value is not empty dictionary

Screenshot 2567-02-07 at 11 37 26

And when I commented out the pagination for 1st view.

class GeneralTradeOrderViewSet(viewsets.GenericViewSet, mixins.ListModelMixin, mixins.RetrieveModelMixin):
    permission_classes = (GeneralTradeOrderPolicy,)
    queryset = Order.objects.all()
    # pagination_class = CustomCursorPagination

The schema is generated correctly for the 2nd view.

Screenshot 2567-02-07 at 11 51 13

Expected behavior The generated schema should be what's in the get_paginated_response_schema

I am not sure if I'm missing some basic steps but this is what I can gather so far.

Atidhaya avatar Feb 07 '24 04:02 Atidhaya

Hi,

spectacular assumes that you only use one paginator for each individual serializer. Otherwise you have a naming collision, and that is what is likely producing your incorrect schema.

You need to create a custom AutoSchema, set it in the settings, and override this method to prevent collisions:

https://github.com/tfranzel/drf-spectacular/blob/21c4e05b8f1c2fb8c47f9d586c6bac8fa31b971d/drf_spectacular/openapi.py#L1146-L1147

You could call this a bug, but I'm not sure how to solve this for everyone, because choosing a good name is not trivial.

tfranzel avatar Feb 07 '24 08:02 tfranzel

That's good to know! I was suspecting it has something to do with the serializer but didn't think of AutoSchema could potentially see 2 different paginator class as being the same.

Thank you for the help and the fast response! 🎉

Atidhaya avatar Feb 07 '24 09:02 Atidhaya