From 446d8bebf69cc49136ac9bd886acc2ed5641483d Mon Sep 17 00:00:00 2001 From: Koudai Aono Date: Sat, 20 Dec 2025 16:18:45 +0000 Subject: [PATCH] fix: skip non-model types in __change_field_name --- src/datamodel_code_generator/parser/base.py | 2 + .../main/graphql/union_snake_case_field.py | 63 +++++++++++++++++++ tests/main/graphql/test_main_graphql.py | 12 ++++ 3 files changed, 77 insertions(+) create mode 100644 tests/data/expected/main/graphql/union_snake_case_field.py diff --git a/src/datamodel_code_generator/parser/base.py b/src/datamodel_code_generator/parser/base.py index 747929fa0..600a2bc49 100644 --- a/src/datamodel_code_generator/parser/base.py +++ b/src/datamodel_code_generator/parser/base.py @@ -1805,6 +1805,8 @@ def __change_field_name( for model in models: if "Enum" in model.base_class: continue + if not model.BASE_CLASS: + continue for field in model.fields: filed_name = field.name diff --git a/tests/data/expected/main/graphql/union_snake_case_field.py b/tests/data/expected/main/graphql/union_snake_case_field.py new file mode 100644 index 000000000..71c9a4e39 --- /dev/null +++ b/tests/data/expected/main/graphql/union_snake_case_field.py @@ -0,0 +1,63 @@ +# generated by datamodel-codegen: +# filename: union.graphql +# timestamp: 2019-07-26T00:00:00+00:00 + +from __future__ import annotations + +from typing import Literal, Union + +from pydantic import BaseModel, Field +from typing_extensions import TypeAliasType + +Boolean = TypeAliasType("Boolean", bool) +""" +The `Boolean` scalar type represents `true` or `false`. +""" + + +ID = TypeAliasType("ID", str) +""" +The `ID` scalar type represents a unique identifier, often used to refetch an object or as key for a cache. The ID type appears in a JSON response as a String; however, it is not intended to be human-readable. When expected as an input type, any string (such as `"4"`) or integer (such as `4`) input value will be accepted as an ID. +""" + + +Int = TypeAliasType("Int", int) +""" +The `Int` scalar type represents non-fractional signed whole numeric values. Int can represent values between -(2^31) and 2^31 - 1. +""" + + +String = TypeAliasType("String", str) +""" +The `String` scalar type represents textual data, represented as UTF-8 character sequences. The String type is most often used by GraphQL to represent free-form human-readable text. +""" + + +class IResource(BaseModel): + id: ID + typename__: Literal['IResource'] | None = Field('IResource', alias='__typename') + + +class Car(IResource): + id: ID + passenger_capacity: Int = Field(..., alias='passengerCapacity') + typename__: Literal['Car'] | None = Field('Car', alias='__typename') + + +class Employee(IResource): + first_name: String | None = Field(None, alias='firstName') + id: ID + last_name: String | None = Field(None, alias='lastName') + typename__: Literal['Employee'] | None = Field('Employee', alias='__typename') + + +Resource = TypeAliasType( + "Resource", + Union[ + 'Car', + 'Employee', + ], +) + + +TechnicalResource = TypeAliasType("TechnicalResource", Car) diff --git a/tests/main/graphql/test_main_graphql.py b/tests/main/graphql/test_main_graphql.py index ed6ef850c..b4c25c7ca 100644 --- a/tests/main/graphql/test_main_graphql.py +++ b/tests/main/graphql/test_main_graphql.py @@ -601,3 +601,15 @@ def test_main_graphql_dataclass_frozen_keyword_only(output_file: Path) -> None: "3.10", ], ) + + +def test_main_graphql_union_snake_case_field(output_file: Path) -> None: + """Test that union type references are not converted to snake_case.""" + run_main_and_assert( + input_path=GRAPHQL_DATA_PATH / "union.graphql", + output_path=output_file, + input_file_type="graphql", + assert_func=assert_file_content, + expected_file="union_snake_case_field.py", + extra_args=["--snake-case-field", "--output-model-type", "pydantic_v2.BaseModel"], + )