Skip to content

Commit d325cf3

Browse files
feat: Add --use-status-code-in-response-name option (#2688)
* feat: Add --use-status-code-in-response-name option to include HTTP status code in response model names * docs: update CLI reference documentation 🤖 Generated by GitHub Actions --------- Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
1 parent 67ea24b commit d325cf3

11 files changed

Lines changed: 202 additions & 2 deletions

File tree

docs/cli-reference/index.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ This documentation is auto-generated from test cases.
1313
| 🏷️ [Field Customization](field-customization.md) | 20 | Field naming and docstring behavior |
1414
| 🏗️ [Model Customization](model-customization.md) | 26 | Model generation behavior |
1515
| 🎨 [Template Customization](template-customization.md) | 16 | Output formatting and custom rendering |
16-
| 📘 [OpenAPI-only Options](openapi-only-options.md) | 5 | OpenAPI-specific features |
16+
| 📘 [OpenAPI-only Options](openapi-only-options.md) | 6 | OpenAPI-specific features |
1717
| ⚙️ [General Options](general-options.md) | 14 | Utilities and meta options |
1818
| 📝 [Utility Options](utility-options.md) | 5 | Help, version, debug options |
1919

@@ -172,6 +172,7 @@ This documentation is auto-generated from test cases.
172172
- [`--use-schema-description`](field-customization.md#use-schema-description)
173173
- [`--use-serialize-as-any`](model-customization.md#use-serialize-as-any)
174174
- [`--use-standard-collections`](typing-customization.md#use-standard-collections)
175+
- [`--use-status-code-in-response-name`](openapi-only-options.md#use-status-code-in-response-name)
175176
- [`--use-subclass-enum`](model-customization.md#use-subclass-enum)
176177
- [`--use-title-as-name`](field-customization.md#use-title-as-name)
177178
- [`--use-type-alias`](typing-customization.md#use-type-alias)

docs/cli-reference/openapi-only-options.md

Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
| [`--openapi-scopes`](#openapi-scopes) | Specify OpenAPI scopes to generate (schemas, paths, paramete... |
99
| [`--read-only-write-only-model-type`](#read-only-write-only-model-type) | Generate separate request and response models for readOnly/w... |
1010
| [`--use-operation-id-as-name`](#use-operation-id-as-name) | Use OpenAPI operationId as the generated function/class name... |
11+
| [`--use-status-code-in-response-name`](#use-status-code-in-response-name) | Include HTTP status code in response model names. |
1112
| [`--validation`](#validation) | Enable validation constraints (deprecated, use --field-const... |
1213

1314
---
@@ -926,6 +927,98 @@ The `--use-operation-id-as-name` flag configures the code generation behavior.
926927

927928
---
928929

930+
## `--use-status-code-in-response-name` {#use-status-code-in-response-name}
931+
932+
Include HTTP status code in response model names.
933+
934+
The `--use-status-code-in-response-name` flag includes the HTTP status code
935+
in generated response model class names. Instead of generating ambiguous names
936+
like ResourceGetResponse, ResourceGetResponse1, ResourceGetResponse2, it generates
937+
clear names like ResourceGetResponse200, ResourceGetResponse400, ResourceGetResponseDefault.
938+
939+
!!! tip "Usage"
940+
941+
```bash
942+
datamodel-codegen --input schema.json --use-status-code-in-response-name --openapi-scopes schemas paths # (1)!
943+
```
944+
945+
1. :material-arrow-left: `--use-status-code-in-response-name` - the option documented here
946+
947+
??? example "Input Schema"
948+
949+
```yaml
950+
openapi: "3.0.0"
951+
info:
952+
version: 1.0.0
953+
title: Status Code Response Name Test API
954+
paths:
955+
/resource:
956+
get:
957+
summary: Get a resource
958+
responses:
959+
'200':
960+
description: Successful response
961+
content:
962+
application/json:
963+
schema:
964+
type: object
965+
properties:
966+
id:
967+
type: integer
968+
name:
969+
type: string
970+
'400':
971+
description: Bad request error
972+
content:
973+
application/json:
974+
schema:
975+
type: object
976+
properties:
977+
error:
978+
type: string
979+
code:
980+
type: integer
981+
'default':
982+
description: Unexpected error
983+
content:
984+
application/json:
985+
schema:
986+
type: object
987+
properties:
988+
message:
989+
type: string
990+
```
991+
992+
??? example "Output"
993+
994+
```python
995+
# generated by datamodel-codegen:
996+
# filename: use_status_code_in_response_name.yaml
997+
# timestamp: 2019-07-26T00:00:00+00:00
998+
999+
from __future__ import annotations
1000+
1001+
from typing import Optional
1002+
1003+
from pydantic import BaseModel
1004+
1005+
1006+
class ResourceGetResponse200(BaseModel):
1007+
id: Optional[int] = None
1008+
name: Optional[str] = None
1009+
1010+
1011+
class ResourceGetResponse400(BaseModel):
1012+
error: Optional[str] = None
1013+
code: Optional[int] = None
1014+
1015+
1016+
class ResourceGetResponseDefault(BaseModel):
1017+
message: Optional[str] = None
1018+
```
1019+
1020+
---
1021+
9291022
## `--validation` {#validation}
9301023

9311024
Enable validation constraints (deprecated, use --field-constraints).

docs/cli-reference/quick-reference.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -128,6 +128,7 @@ datamodel-codegen [OPTIONS]
128128
| [`--openapi-scopes`](openapi-only-options.md#openapi-scopes) | Specify OpenAPI scopes to generate (schemas, paths, parameters). |
129129
| [`--read-only-write-only-model-type`](openapi-only-options.md#read-only-write-only-model-type) | Generate separate request and response models for readOnly/writeOnly fields. |
130130
| [`--use-operation-id-as-name`](openapi-only-options.md#use-operation-id-as-name) | Use OpenAPI operationId as the generated function/class name. |
131+
| [`--use-status-code-in-response-name`](openapi-only-options.md#use-status-code-in-response-name) | Include HTTP status code in response model names. |
131132
| [`--validation`](openapi-only-options.md#validation) | Enable validation constraints (deprecated, use --field-constraints). |
132133

133134
### ⚙️ General Options
@@ -262,6 +263,7 @@ All options sorted alphabetically:
262263
- [`--use-schema-description`](field-customization.md#use-schema-description) - Use schema description as class docstring.
263264
- [`--use-serialize-as-any`](model-customization.md#use-serialize-as-any) - Wrap fields with subtypes in Pydantic's SerializeAsAny.
264265
- [`--use-standard-collections`](typing-customization.md#use-standard-collections) - Use built-in dict/list instead of typing.Dict/List.
266+
- [`--use-status-code-in-response-name`](openapi-only-options.md#use-status-code-in-response-name) - Include HTTP status code in response model names.
265267
- [`--use-subclass-enum`](model-customization.md#use-subclass-enum) - Generate typed Enum subclasses for enums with specific field...
266268
- [`--use-title-as-name`](field-customization.md#use-title-as-name) - Use schema title as the generated class name.
267269
- [`--use-type-alias`](typing-customization.md#use-type-alias) - Use TypeAlias instead of root models for type definitions (e...

src/datamodel_code_generator/__init__.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -477,6 +477,7 @@ def generate( # noqa: PLR0912, PLR0913, PLR0914, PLR0915
477477
disable_future_imports: bool = False,
478478
type_mappings: list[str] | None = None,
479479
read_only_write_only_model_type: ReadOnlyWriteOnlyModelType | None = None,
480+
use_status_code_in_response_name: bool = False,
480481
all_exports_scope: AllExportsScope | None = None,
481482
all_exports_collision_strategy: AllExportsCollisionStrategy | None = None,
482483
module_split_mode: ModuleSplitMode | None = None,
@@ -536,6 +537,7 @@ def generate( # noqa: PLR0912, PLR0913, PLR0914, PLR0915
536537
parser_class: type[Parser] = OpenAPIParser
537538
kwargs["openapi_scopes"] = openapi_scopes
538539
kwargs["include_path_parameters"] = include_path_parameters
540+
kwargs["use_status_code_in_response_name"] = use_status_code_in_response_name
539541
elif input_file_type == InputFileType.GraphQL:
540542
from datamodel_code_generator.parser.graphql import GraphQLParser # noqa: PLC0415
541543

src/datamodel_code_generator/__main__.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -463,6 +463,7 @@ def validate_all_exports_collision_strategy(cls, values: dict[str, Any]) -> dict
463463
disable_future_imports: bool = False
464464
type_mappings: Optional[list[str]] = None # noqa: UP045
465465
read_only_write_only_model_type: Optional[ReadOnlyWriteOnlyModelType] = None # noqa: UP045
466+
use_status_code_in_response_name: bool = False
466467
all_exports_scope: Optional[AllExportsScope] = None # noqa: UP045
467468
all_exports_collision_strategy: Optional[AllExportsCollisionStrategy] = None # noqa: UP045
468469
module_split_mode: Optional[ModuleSplitMode] = None # noqa: UP045
@@ -765,6 +766,7 @@ def run_generate_from_config( # noqa: PLR0913, PLR0917
765766
disable_future_imports=config.disable_future_imports,
766767
type_mappings=config.type_mappings,
767768
read_only_write_only_model_type=config.read_only_write_only_model_type,
769+
use_status_code_in_response_name=config.use_status_code_in_response_name,
768770
all_exports_scope=config.all_exports_scope,
769771
all_exports_collision_strategy=config.all_exports_collision_strategy,
770772
module_split_mode=config.module_split_mode,

src/datamodel_code_generator/arguments.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -713,6 +713,12 @@ def start_section(self, heading: str | None) -> None:
713713
choices=[e.value for e in ReadOnlyWriteOnlyModelType],
714714
default=None,
715715
)
716+
openapi_options.add_argument(
717+
"--use-status-code-in-response-name",
718+
help="Include HTTP status code in response model names (e.g., ResourceGetResponse200, ResourceGetResponseDefault)",
719+
action="store_true",
720+
default=None,
721+
)
716722

717723
# ======================================================================================
718724
# General options

src/datamodel_code_generator/cli_options.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -181,6 +181,9 @@ class CLIOptionMeta:
181181
# ==========================================================================
182182
"--openapi-scopes": CLIOptionMeta(name="--openapi-scopes", category=OptionCategory.OPENAPI),
183183
"--use-operation-id-as-name": CLIOptionMeta(name="--use-operation-id-as-name", category=OptionCategory.OPENAPI),
184+
"--use-status-code-in-response-name": CLIOptionMeta(
185+
name="--use-status-code-in-response-name", category=OptionCategory.OPENAPI
186+
),
184187
"--read-only-write-only-model-type": CLIOptionMeta(
185188
name="--read-only-write-only-model-type", category=OptionCategory.OPENAPI
186189
),

src/datamodel_code_generator/parser/openapi.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -272,6 +272,7 @@ def __init__( # noqa: PLR0913
272272
type_mappings: list[str] | None = None,
273273
read_only_write_only_model_type: ReadOnlyWriteOnlyModelType | None = None,
274274
use_frozen_field: bool = False,
275+
use_status_code_in_response_name: bool = False,
275276
) -> None:
276277
"""Initialize the OpenAPI parser with extensive configuration options."""
277278
target_datetime_class = target_datetime_class or DatetimeClassType.Awaredatetime
@@ -369,6 +370,7 @@ def __init__( # noqa: PLR0913
369370
)
370371
self.open_api_scopes: list[OpenAPIScope] = openapi_scopes or [OpenAPIScope.Schemas]
371372
self.include_path_parameters: bool = include_path_parameters
373+
self.use_status_code_in_response_name: bool = use_status_code_in_response_name
372374
self._discriminator_schemas: dict[str, dict[str, Any]] = {}
373375
self._discriminator_subtypes: dict[str, list[str]] = defaultdict(list)
374376

@@ -550,6 +552,8 @@ def parse_responses(
550552
"""Parse response objects into data types by status code and content type."""
551553
data_types: defaultdict[str | int, dict[str, DataType]] = defaultdict(dict)
552554
for status_code, detail in responses.items():
555+
response_name = f"{name}{str(status_code).capitalize()}" if self.use_status_code_in_response_name else name
556+
553557
if isinstance(detail, ReferenceObject):
554558
if not detail.ref: # pragma: no cover
555559
continue
@@ -563,7 +567,7 @@ def parse_responses(
563567

564568
for content_type, obj in content.items():
565569
response_path: list[str] = [*path, str(status_code), str(content_type)]
566-
data_type = self._parse_schema_or_ref(name, obj.schema_, response_path)
570+
data_type = self._parse_schema_or_ref(response_name, obj.schema_, response_path)
567571
if data_type:
568572
data_types[status_code][content_type] = data_type # pyright: ignore[reportArgumentType]
569573

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
# generated by datamodel-codegen:
2+
# filename: use_status_code_in_response_name.yaml
3+
# timestamp: 2019-07-26T00:00:00+00:00
4+
5+
from __future__ import annotations
6+
7+
from typing import Optional
8+
9+
from pydantic import BaseModel
10+
11+
12+
class ResourceGetResponse200(BaseModel):
13+
id: Optional[int] = None
14+
name: Optional[str] = None
15+
16+
17+
class ResourceGetResponse400(BaseModel):
18+
error: Optional[str] = None
19+
code: Optional[int] = None
20+
21+
22+
class ResourceGetResponseDefault(BaseModel):
23+
message: Optional[str] = None
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
openapi: "3.0.0"
2+
info:
3+
version: 1.0.0
4+
title: Status Code Response Name Test API
5+
paths:
6+
/resource:
7+
get:
8+
summary: Get a resource
9+
responses:
10+
'200':
11+
description: Successful response
12+
content:
13+
application/json:
14+
schema:
15+
type: object
16+
properties:
17+
id:
18+
type: integer
19+
name:
20+
type: string
21+
'400':
22+
description: Bad request error
23+
content:
24+
application/json:
25+
schema:
26+
type: object
27+
properties:
28+
error:
29+
type: string
30+
code:
31+
type: integer
32+
'default':
33+
description: Unexpected error
34+
content:
35+
application/json:
36+
schema:
37+
type: object
38+
properties:
39+
message:
40+
type: string

0 commit comments

Comments
 (0)