diff --git a/rest_framework/schemas/openapi.py b/rest_framework/schemas/openapi.py index 6bed12092..1d0ec35d5 100644 --- a/rest_framework/schemas/openapi.py +++ b/rest_framework/schemas/openapi.py @@ -131,11 +131,11 @@ class AutoSchema(ViewInspector): response_media_types = [] method_mapping = { - 'get': 'Retrieve', - 'post': 'Create', - 'put': 'Update', - 'patch': 'PartialUpdate', - 'delete': 'Destroy', + 'get': 'retrieve', + 'post': 'create', + 'put': 'update', + 'patch': 'partialUpdate', + 'delete': 'destroy', } def get_operation(self, path, method): @@ -195,6 +195,12 @@ class AutoSchema(ViewInspector): content = self._map_serializer(serializer) return {component_name: content} + def _to_camel_case(self, snake_str): + components = snake_str.split('_') + # We capitalize the first letter of each component except the first one + # with the 'title' method and join them together. + return components[0] + ''.join(x.title() for x in components[1:]) + def get_operation_id_base(self, path, method, action): """ Compute the base part for operation ID from the model, serializer or view name. @@ -240,7 +246,7 @@ class AutoSchema(ViewInspector): if is_list_view(path, method, self.view): action = 'list' elif method_name not in self.method_mapping: - action = method_name + action = self._to_camel_case(method_name) else: action = self.method_mapping[method.lower()] diff --git a/tests/schemas/test_openapi.py b/tests/schemas/test_openapi.py index 35d676d6c..c9f6d967e 100644 --- a/tests/schemas/test_openapi.py +++ b/tests/schemas/test_openapi.py @@ -158,7 +158,7 @@ class TestOperationIntrospection(TestCase): operation = inspector.get_operation(path, method) assert operation == { - 'operationId': 'RetrieveDocStringExampleDetail', + 'operationId': 'retrieveDocStringExampleDetail', 'description': 'A description of my GET operation.', 'parameters': [{ 'description': '', @@ -735,6 +735,23 @@ class TestOperationIntrospection(TestCase): print(str(w[-1].message)) assert 'You have a duplicated operationId' in str(w[-1].message) + def test_operation_id_viewset(self): + router = routers.SimpleRouter() + router.register('account', views.ExampleViewSet, basename="account") + urlpatterns = router.urls + + generator = SchemaGenerator(patterns=urlpatterns) + + request = create_request('/') + schema = generator.get_schema(request=request) + print(schema) + assert schema['paths']['/account/']['get']['operationId'] == 'listExampleViewSets' + assert schema['paths']['/account/']['post']['operationId'] == 'createExampleViewSet' + assert schema['paths']['/account/{id}/']['get']['operationId'] == 'retrieveExampleViewSet' + assert schema['paths']['/account/{id}/']['put']['operationId'] == 'updateExampleViewSet' + assert schema['paths']['/account/{id}/']['patch']['operationId'] == 'partialUpdateExampleViewSet' + assert schema['paths']['/account/{id}/']['delete']['operationId'] == 'destroyExampleViewSet' + def test_serializer_datefield(self): path = '/' method = 'GET' diff --git a/tests/schemas/views.py b/tests/schemas/views.py index 1c8235b42..5645f59bf 100644 --- a/tests/schemas/views.py +++ b/tests/schemas/views.py @@ -11,7 +11,7 @@ from rest_framework.decorators import action from rest_framework.response import Response from rest_framework.schemas.openapi import AutoSchema from rest_framework.views import APIView -from rest_framework.viewsets import GenericViewSet +from rest_framework.viewsets import GenericViewSet, ViewSet class ExampleListView(APIView): @@ -215,3 +215,25 @@ class ExampleAutoSchemaDuplicate2(generics.GenericAPIView): serializer = self.get_serializer(data=now.date(), datetime=now) return Response(serializer.data) + + +class ExampleViewSet(ViewSet): + serializer_class = ExampleSerializerModel + + def list(self, request): + pass + + def create(self, request): + pass + + def retrieve(self, request, pk=None): + pass + + def update(self, request, pk=None): + pass + + def partial_update(self, request, pk=None): + pass + + def destroy(self, request, pk=None): + pass