diff --git a/drf_spectacular/openapi.py b/drf_spectacular/openapi.py index f4f14f3b..106fb848 100644 --- a/drf_spectacular/openapi.py +++ b/drf_spectacular/openapi.py @@ -460,7 +460,12 @@ def get_operation_id(self) -> str: if re.search(r'', self.path_regex): tokenized_path.append('formatted') - return '_'.join(tokenized_path + [action]) + if spectacular_settings.OPERATION_ID_METHOD_POSITION == 'PRE': + return '_'.join([action] + tokenized_path) + elif spectacular_settings.OPERATION_ID_METHOD_POSITION == 'POST': + return '_'.join(tokenized_path + [action]) + else: + assert False, 'Invalid value for OPERATION_ID_METHOD_POSITION. Allowed: PRE, POST' def is_deprecated(self) -> bool: """ override this for custom behaviour """ diff --git a/drf_spectacular/settings.py b/drf_spectacular/settings.py index 61017db9..075f3c20 100644 --- a/drf_spectacular/settings.py +++ b/drf_spectacular/settings.py @@ -139,6 +139,11 @@ # ``djangorestframework_camel_case``, while CAMELIZE_NAMES itself does not. 'CAMELIZE_NAMES': False, + # Changes the location of the action/method on the generated OperationId. For example, + # "POST": "group_person_list", "group_person_create" + # "PRE": "list_group_person", "create_group_person" + 'OPERATION_ID_METHOD_POSITION': 'POST', + # Determines if and how free-form 'additionalProperties' should be emitted in the schema. Some # code generator targets are sensitive to this. None disables generic 'additionalProperties'. # allowed values are 'dict', 'bool', None diff --git a/tests/test_regressions.py b/tests/test_regressions.py index 3fe0bf76..806e43f5 100644 --- a/tests/test_regressions.py +++ b/tests/test_regressions.py @@ -3410,3 +3410,14 @@ class XViewset(viewsets.ModelViewSet): assert schema['components']['schemas']['X']["properties"]["field_shirts"] == { "readOnly": True, 'type': 'string' } + + +@mock.patch('drf_spectacular.settings.spectacular_settings.OPERATION_ID_METHOD_POSITION', "PRE") +def test_operation_id_method_position(no_warnings): + class XViewSet(viewsets.ReadOnlyModelViewSet): + queryset = SimpleModel.objects.all() + serializer_class = SimpleSerializer + + schema = generate_schema('/x', XViewSet) + assert schema['paths']['/x/']['get']["operationId"] == 'list_x' + assert schema['paths']['/x/{id}/']['get']["operationId"] == 'retrieve_x'