From 4359b9355f7b8d2529683a6599dce63dc1a0ad0b Mon Sep 17 00:00:00 2001 From: Emanuel Ilyayev Date: Tue, 21 Nov 2023 21:44:58 -0500 Subject: [PATCH 1/4] Fix issue #4740 https://github.com/tiangolo/fastapi/issues/4740 --- fastapi/openapi/utils.py | 2 ++ fastapi/utils.py | 3 ++- tests/main.py | 5 ++++ tests/test_application.py | 48 ++++++++++++++++++++++++++++++--------- 4 files changed, 46 insertions(+), 12 deletions(-) diff --git a/fastapi/openapi/utils.py b/fastapi/openapi/utils.py index 5bfb5acef..6c4a1bd7f 100644 --- a/fastapi/openapi/utils.py +++ b/fastapi/openapi/utils.py @@ -196,6 +196,8 @@ def get_openapi_operation_metadata( if route.description: operation["description"] = route.description operation_id = route.operation_id or route.unique_id + if len(route.methods) > 1: + operation_id = f'{operation_id}_{method.lower()}' if operation_id in operation_ids: message = ( f"Duplicate Operation ID {operation_id} for function " diff --git a/fastapi/utils.py b/fastapi/utils.py index f8463dda2..d617b8530 100644 --- a/fastapi/utils.py +++ b/fastapi/utils.py @@ -183,7 +183,8 @@ def generate_unique_id(route: "APIRoute") -> str: operation_id = route.name + route.path_format operation_id = re.sub(r"\W", "_", operation_id) assert route.methods - operation_id = operation_id + "_" + list(route.methods)[0].lower() + if len(route.methods) == 1: + operation_id = operation_id + "_" + list(route.methods)[0].lower() return operation_id diff --git a/tests/main.py b/tests/main.py index 15760c039..48452729b 100644 --- a/tests/main.py +++ b/tests/main.py @@ -192,3 +192,8 @@ def get_enum_status_code(): @app.get("/query/frozenset") def get_query_type_frozenset(query: FrozenSet[int] = Query(...)): return ",".join(map(str, sorted(query))) + + +@app.api_route("/multiple-methods", methods=["GET", "POST"]) +def multiple_methods(): + return {"message": "Hello World"} diff --git a/tests/test_application.py b/tests/test_application.py index ea7a80128..311ceaa85 100644 --- a/tests/test_application.py +++ b/tests/test_application.py @@ -1163,9 +1163,46 @@ def test_openapi_schema(): }, } }, + "/multiple-methods": { + "get": { + "summary": "Multiple Methods", + "operationId": "multiple_methods_multiple_methods_get", + "responses": { + "200": { + "description": "Successful Response", + "content": {"application/json": {"schema": {}}} + } + } + }, + "post": { + "summary": "Multiple Methods", + "operationId": "multiple_methods_multiple_methods_post", + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": {} + } + } + } + } + } + } }, "components": { "schemas": { + "HTTPValidationError": { + "title": "HTTPValidationError", + "type": "object", + "properties": { + "detail": { + "title": "Detail", + "type": "array", + "items": {"$ref": "#/components/schemas/ValidationError"}, + } + }, + }, "ValidationError": { "title": "ValidationError", "required": ["loc", "msg", "type"], @@ -1182,17 +1219,6 @@ def test_openapi_schema(): "type": {"title": "Error Type", "type": "string"}, }, }, - "HTTPValidationError": { - "title": "HTTPValidationError", - "type": "object", - "properties": { - "detail": { - "title": "Detail", - "type": "array", - "items": {"$ref": "#/components/schemas/ValidationError"}, - } - }, - }, } }, } From e899edef4a3ab5eba5cdf0c1853d83faafefbd30 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Wed, 22 Nov 2023 03:13:48 +0000 Subject: [PATCH 2/4] =?UTF-8?q?=F0=9F=8E=A8=20[pre-commit.ci]=20Auto=20for?= =?UTF-8?q?mat=20from=20pre-commit.com=20hooks?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- fastapi/openapi/utils.py | 2 +- tests/test_application.py | 16 ++++++---------- 2 files changed, 7 insertions(+), 11 deletions(-) diff --git a/fastapi/openapi/utils.py b/fastapi/openapi/utils.py index 6c4a1bd7f..ac8f76149 100644 --- a/fastapi/openapi/utils.py +++ b/fastapi/openapi/utils.py @@ -197,7 +197,7 @@ def get_openapi_operation_metadata( operation["description"] = route.description operation_id = route.operation_id or route.unique_id if len(route.methods) > 1: - operation_id = f'{operation_id}_{method.lower()}' + operation_id = f"{operation_id}_{method.lower()}" if operation_id in operation_ids: message = ( f"Duplicate Operation ID {operation_id} for function " diff --git a/tests/test_application.py b/tests/test_application.py index 311ceaa85..67f07e51d 100644 --- a/tests/test_application.py +++ b/tests/test_application.py @@ -1170,9 +1170,9 @@ def test_openapi_schema(): "responses": { "200": { "description": "Successful Response", - "content": {"application/json": {"schema": {}}} + "content": {"application/json": {"schema": {}}}, } - } + }, }, "post": { "summary": "Multiple Methods", @@ -1180,15 +1180,11 @@ def test_openapi_schema(): "responses": { "200": { "description": "Successful Response", - "content": { - "application/json": { - "schema": {} - } - } + "content": {"application/json": {"schema": {}}}, } - } - } - } + }, + }, + }, }, "components": { "schemas": { From 3e6a63e4c90fbbc0f8ed8242392a88c04bcb2b7c Mon Sep 17 00:00:00 2001 From: Emanuel Ilyayev Date: Tue, 21 Nov 2023 22:35:52 -0500 Subject: [PATCH 3/4] update test coverage --- tests/test_application.py | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/test_application.py b/tests/test_application.py index 311ceaa85..690258ba0 100644 --- a/tests/test_application.py +++ b/tests/test_application.py @@ -12,6 +12,7 @@ client = TestClient(app) [ ("/api_route", 200, {"message": "Hello World"}), ("/non_decorated_route", 200, {"message": "Hello World"}), + ("/multiple-methods", 200, {"message": "Hello World"}), ("/nonexistent", 404, {"detail": "Not Found"}), ], ) From e03b302d97c993be0f0f163eab703fd184e29a65 Mon Sep 17 00:00:00 2001 From: Yurii Motov Date: Sat, 14 Jun 2025 09:28:49 +0200 Subject: [PATCH 4/4] fix missed close bracket in test_application.py --- tests/test_application.py | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/test_application.py b/tests/test_application.py index 366338184..5f3d97c04 100644 --- a/tests/test_application.py +++ b/tests/test_application.py @@ -1185,6 +1185,7 @@ def test_openapi_schema(): } }, }, + }, "/query/list": { "get": { "summary": "Get Query List",