diff --git a/fastapi/openapi/utils.py b/fastapi/openapi/utils.py index 8f1852b0cc..07791fca47 100644 --- a/fastapi/openapi/utils.py +++ b/fastapi/openapi/utils.py @@ -33,6 +33,7 @@ from fastapi.types import ModelNameMap from fastapi.utils import ( deep_dict_update, generate_operation_id_for_path, + generate_unique_id, is_body_allowed_for_status_code, ) from pydantic import BaseModel @@ -242,7 +243,7 @@ def get_openapi_operation_metadata( operation["summary"] = generate_operation_summary(route=route, method=method) if route.description: operation["description"] = route.description - operation_id = route.operation_id or route.unique_id + operation_id = route.operation_id or generate_unique_id(route, method=method) if operation_id in operation_ids: endpoint_name = getattr(route.endpoint, "__name__", "") message = f"Duplicate Operation ID {operation_id} for function {endpoint_name}" diff --git a/fastapi/utils.py b/fastapi/utils.py index 12eaa2bf08..575af49cda 100644 --- a/fastapi/utils.py +++ b/fastapi/utils.py @@ -92,11 +92,17 @@ def generate_operation_id_for_path( return operation_id -def generate_unique_id(route: "APIRoute") -> str: +def generate_unique_id(route: "APIRoute", method: str | None = None) -> str: operation_id = f"{route.name}{route.path_format}" operation_id = re.sub(r"\W", "_", operation_id) assert route.methods - operation_id = f"{operation_id}_{list(route.methods)[0].lower()}" + if method: + # Include the specific method being generated + operation_id = f"{operation_id}_{method.lower()}" + else: + # When no specific method is provided, include all methods (for backwards compatibility) + methods_str = "_".join(sorted(m.lower() for m in route.methods)) + operation_id = f"{operation_id}_{methods_str}" return operation_id