Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Integration with drf-rw-serializers #448

Closed
luzfcb opened this issue Jul 1, 2021 · 4 comments
Closed

Integration with drf-rw-serializers #448

luzfcb opened this issue Jul 1, 2021 · 4 comments

Comments

@luzfcb
Copy link

luzfcb commented Jul 1, 2021

Describe the bug
Hello, first of all, thanks for your work. This library is being very useful for one of the projects I'm working on.

One of the projects I work on, we make extensive use of drf-rw-serializers.

This means that serializer_class is not defined in the Views, and instead, write_serializer_class and read_serializer_class are defined.

All view classes provided by drf-rw-serializers inherit from drf-rw-serializers GenericAPIView class https://github.com/vintasoftware/drf-rw-serializers/blob/master/drf_rw_serializers/generics.py#L8-L71

I'm trying to get the documentation generated correctly, but I haven't been able to succeed yet.

The generated schema does not include any information provided by serializers:

  /api/mymodel/:
    post:
      operationId: mymodel_create
      description: ''
      tags:
      - core
      security:
      - cookieAuth: []
      - basicAuth: []
      responses:
        '201':
          description: No response body
  /api/mymodel/{id}/:
    get:
      operationId: mymodel_retrieve
      description: ''
      parameters:
      - in: path
        name: id
        schema:
          type: integer
        required: true
      tags:
      - core
      security:
      - cookieAuth: []
      - basicAuth: []
      responses:
        '200':
          description: No response body

Below is the minimum code to reproduce the problem.

To Reproduce

from rest_framework.permissions import IsAdminUser
from rest_framework import parsers
from drf_rw_serializers.viewsets import ModelViewSet as ModelRWViewSet
from drf_spectacular.utils import extend_schema, extend_schema_view

from .models import MyModel
from .serializers import MyModelUploadSerializer, MyModelSerializer

@extend_schema_view(
    post=extend_schema(
        description="POST method description here",
        request=MyModelUploadSerializer,
        responses={
            201: MyModelSerializer
        }
    ),
    get=extend_schema(
        description="GET method description here",
        responses={
            200: MyModelSerializer
        }
    )
)
class MyAPIView(ModelRWViewSet):
    queryset = MyModel.objects.order_by('-created').all()
    write_serializer_class = MyModelUploadSerializer
    read_serializer_class = MyModelSerializer
    permission_classes = [IsAdminUser]
    parser_classes = [parsers.MultiPartParser]

urls.py

from django.urls import path
from .api_views import MyAPIView

urlpatterns = [
    path('api/mymodel/', view=MyAPIView.as_view({'post': 'create'}),
         name='mymodel-upload'
         ),
    path('api/mymodel/<int:pk>/', view=MyAPIView.as_view({'get': 'retrieve'}),
         name='mymodel-detail'
         ),
]

Expected behavior

I would hope to get some hint on how to implement a generic way to make drf-spectacular understand the structure of a view that inherits from drf-rw-serializers GenericAPIView class and from there, be able to use write_serializer_class and read_serializer_class instead of just serializer_class to generate the OpenAPI schema.

@tfranzel
Copy link
Owner

tfranzel commented Jul 1, 2021

hi @luzfcb,

that's a pretty neat library i never heard about. nice and very useful. regarding your schema not changing

https://drf-spectacular.readthedocs.io/en/latest/faq.html#using-extend-schema-on-apiview-has-no-effect

so i'm pretty sure you used the wrong method names for @extend_schema_view. for viewsets it must be create and list/retrieve not post etc. for a APIView it would be correct like that.

if you use this a lot i would recommend having a patched AutoSchema that accounts for this lib. something like

class CustomSchema(AutoSchema):

    def get_request_serializer(self):
        """ override this for custom behaviour """
        if isinstance(self.view, drf_rw_serializers.GenericAPIView):
             return view.get_write_serializer_class()()
        return self._get_serializer()

    def get_response_serializers(self):
        """ override this for custom behaviour """
        if isinstance(self.view, drf_rw_serializers.GenericAPIView):
             return view.get_read_serializer_class()()
        return self._get_serializer()

this would probably need some refinement but should point you in the right direction. don't forget to set this AutoSchema in the settings.

@tfranzel
Copy link
Owner

tfranzel commented Jul 1, 2021

this does not really fit into the core but we can add a blueprint to the docs and link in on the global Readme if you care to do a PR

@luzfcb
Copy link
Author

luzfcb commented Jul 2, 2021

@tfranzel thanks for pointing out my error in using the wrong method names with viewsets... my initial implementation didn't use viewsets and I hadn't read the FAQ documentation before. Sorry about that.

Thanks for the tip to customize AutoSchema, it worked almost without changes.

from drf_spectacular.openapi import AutoSchema
from drf_rw_serializers.generics import GenericAPIView as RWGenericAPIView


class CustomAutoSchema(AutoSchema):
    def get_request_serializer(self):
        if isinstance(self.view, RWGenericAPIView):
            return self.view.get_write_serializer_class()()
        return self._get_serializer()

    def get_response_serializers(self):
        if isinstance(self.view, RWGenericAPIView):
            return self.view.get_read_serializer_class()()
        return self._get_serializer()

On your settings.py:

REST_FRAMEWORK = {
    # ...
    'DEFAULT_SCHEMA_CLASS': 'path.to.your.openapi.CustomAutoSchema',
}

I plan to create a pull-request to include this example in the documentation

@tfranzel
Copy link
Owner

tfranzel commented Jul 3, 2021

awsome! it should probably go in here https://drf-spectacular.readthedocs.io/en/latest/blueprints.html with a link in the README. at least that is how i would imagine it.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants