Skip to content

Commit 6ba81b2

Browse files
committed
Update accoriding to PR's requests
* rename operation_name with operation_id_base * add get_operation_id_base * get_operation_id not private * update docs * update tests
1 parent 93d9897 commit 6ba81b2

File tree

3 files changed

+96
-20
lines changed

3 files changed

+96
-20
lines changed

docs/api-guide/schemas.md

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -227,11 +227,28 @@ from rest_framework.schemas.openapi import AutoSchema
227227

228228
class ExampleView(APIView):
229229
"""APIView subclass with custom schema introspection."""
230-
schema = AutoSchema(operation_name="Custom")
230+
schema = AutoSchema(operation_id_base="Custom")
231231
```
232232

233233
The previous example will generate the following operationid: "ListCustoms", "RetrieveCustom", "UpdateCustom", "PartialUpdateCustom", "DestroyCustom".
234234

235+
You need to provide the singular form of he operation name. For the list operation, a "s" will be append at the end of the name.
236+
237+
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.
238+
239+
```python
240+
class CustomSchema(AutoSchema):
241+
def get_operation_id_base(self, action):
242+
pass
243+
244+
def get_operation_id(self, path, method):
245+
pass
246+
247+
class CustomView(APIView):
248+
"""APIView subclass with custom schema introspection."""
249+
schema = CustomSchema()
250+
```
251+
235252
[openapi]: https://github.com/OAI/OpenAPI-Specification
236253
[openapi-specification-extensions]: https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.2.md#specification-extensions
237254
[openapi-operation]: https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.2.md#operationObject

rest_framework/schemas/openapi.py

Lines changed: 24 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -80,17 +80,17 @@ class AutoSchema(ViewInspector):
8080
'delete': 'Destroy',
8181
}
8282

83-
def __init__(self, operation_name=None):
83+
def __init__(self, operation_id_base=None):
8484
"""
85-
:param operation_name: user-defined name in operationId. If empty, it will be deducted from the Model/Serializer/View name.
85+
:param operation_id_base: user-defined name in operationId. If empty, it will be deducted from the Model/Serializer/View name.
8686
"""
8787
super().__init__()
88-
self.operation_name = operation_name
88+
self.operation_id_base = operation_id_base
8989

9090
def get_operation(self, path, method):
9191
operation = {}
9292

93-
operation['operationId'] = self._get_operation_id(path, method)
93+
operation['operationId'] = self.get_operation_id(path, method)
9494
operation['description'] = self.get_description(path, method)
9595

9696
parameters = []
@@ -106,22 +106,14 @@ def get_operation(self, path, method):
106106

107107
return operation
108108

109-
def _get_operation_id(self, path, method):
109+
def get_operation_id_base(self, action):
110110
"""
111-
Compute an operation ID from the model, serializer or view name.
111+
Compute the base part for operation ID from the model, serializer or view name.
112112
"""
113-
method_name = getattr(self.view, 'action', method.lower())
114-
if is_list_view(path, method, self.view):
115-
action = 'list'
116-
elif method_name not in self.method_mapping:
117-
action = method_name
118-
else:
119-
action = self.method_mapping[method.lower()]
120-
121113
model = getattr(getattr(self.view, 'queryset', None), 'model', None)
122114

123-
if self.operation_name is not None:
124-
name = self.operation_name
115+
if self.operation_id_base is not None:
116+
name = self.operation_id_base
125117

126118
# Try to deduce the ID from the view's model
127119
elif model is not None:
@@ -149,6 +141,22 @@ def _get_operation_id(self, path, method):
149141
if action == 'list' and not name.endswith('s'): # listThings instead of listThing
150142
name += 's'
151143

144+
return name
145+
146+
def get_operation_id(self, path, method):
147+
"""
148+
Compute an operation ID from the view type and get_operation_id_base method.
149+
"""
150+
method_name = getattr(self.view, 'action', method.lower())
151+
if is_list_view(path, method, self.view):
152+
action = 'list'
153+
elif method_name not in self.method_mapping:
154+
action = method_name
155+
else:
156+
action = self.method_mapping[method.lower()]
157+
158+
name = self.get_operation_id_base(action)
159+
152160
return action + name
153161

154162
def _get_path_parameters(self, path, method):

tests/schemas/test_openapi.py

Lines changed: 54 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -521,9 +521,24 @@ def test_operation_id_generation(self):
521521
inspector = AutoSchema()
522522
inspector.view = view
523523

524-
operationId = inspector._get_operation_id(path, method)
524+
operationId = inspector.get_operation_id(path, method)
525525
assert operationId == 'listExamples'
526526

527+
def test_operation_id_custom_operation_id_base(self):
528+
path = '/'
529+
method = 'GET'
530+
531+
view = create_view(
532+
views.ExampleGenericAPIView,
533+
method,
534+
create_request(path),
535+
)
536+
inspector = AutoSchema(operation_id_base="Ulysse")
537+
inspector.view = view
538+
539+
operationId = inspector.get_operation_id(path, method)
540+
assert operationId == 'listUlysses'
541+
527542
def test_operation_id_custom_name(self):
528543
path = '/'
529544
method = 'GET'
@@ -533,12 +548,48 @@ def test_operation_id_custom_name(self):
533548
method,
534549
create_request(path),
535550
)
536-
inspector = AutoSchema(operation_name="Ulysse")
551+
inspector = AutoSchema(operation_id_base='Ulysse')
537552
inspector.view = view
538553

539-
operationId = inspector._get_operation_id(path, method)
554+
operationId = inspector.get_operation_id(path, method)
540555
assert operationId == 'listUlysses'
541556

557+
def test_operation_id_override_get(self):
558+
class CustomSchema(AutoSchema):
559+
def get_operation_id(self, path, method):
560+
return 'myCustomOperationId'
561+
562+
path = '/'
563+
method = 'GET'
564+
view = create_view(
565+
views.ExampleGenericAPIView,
566+
method,
567+
create_request(path),
568+
)
569+
inspector = CustomSchema()
570+
inspector.view = view
571+
572+
operationId = inspector.get_operation_id(path, method)
573+
assert operationId == 'myCustomOperationId'
574+
575+
def test_operation_id_override_base(self):
576+
class CustomSchema(AutoSchema):
577+
def get_operation_id_base(self, action):
578+
return 'Item'
579+
580+
path = '/'
581+
method = 'GET'
582+
view = create_view(
583+
views.ExampleGenericAPIView,
584+
method,
585+
create_request(path),
586+
)
587+
inspector = CustomSchema()
588+
inspector.view = view
589+
590+
operationId = inspector.get_operation_id(path, method)
591+
assert operationId == 'listItem'
592+
542593
def test_repeat_operation_ids(self):
543594
router = routers.SimpleRouter()
544595
router.register('account', views.ExampleGenericViewSet, basename="account")

0 commit comments

Comments
 (0)