mirror of
https://github.com/encode/django-rest-framework.git
synced 2024-11-25 02:53:58 +03:00
OpenAPI: Make operationId camelCase, matching spec examples. (#7208)
This commit is contained in:
parent
609f708a27
commit
6a23fa0649
|
@ -290,7 +290,8 @@ class MyView(APIView):
|
|||
|
||||
### OperationId
|
||||
|
||||
The schema generator generates an [operationid](openapi-operationid) for each operation. This `operationId` is deduced from the model name, serializer name or view name. The operationId may looks like "ListItems", "RetrieveItem", "UpdateItem", etc..
|
||||
The schema generator generates an [operationid](openapi-operationid) for each operation. This `operationId` is deduced from the model name, serializer name or view name. The operationId may looks like "listItems", "retrieveItem", "updateItem", etc..
|
||||
The `operationId` is camelCase by convention.
|
||||
|
||||
If you have several views with the same model, the generator may generate duplicate operationId.
|
||||
In order to work around this, you can override the second part of the operationId: operation name.
|
||||
|
@ -303,7 +304,7 @@ class ExampleView(APIView):
|
|||
schema = AutoSchema(operation_id_base="Custom")
|
||||
```
|
||||
|
||||
The previous example will generate the following operationId: "ListCustoms", "RetrieveCustom", "UpdateCustom", "PartialUpdateCustom", "DestroyCustom".
|
||||
The previous example will generate the following operationId: "listCustoms", "retrieveCustom", "updateCustom", "partialUpdateCustom", "destroyCustom".
|
||||
You need to provide the singular form of he operation name. For the list operation, a "s" will be appended at the end of the operation.
|
||||
|
||||
If you need more configuration over the `operationId` field, you can override the `get_operation_id_base` and `get_operation_id` methods from the `AutoSchema` class:
|
||||
|
|
|
@ -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()]
|
||||
|
||||
|
|
|
@ -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'
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in New Issue
Block a user