Repository: koxudaxi/datamodel-code-generator (2025-06-04 ~ 2025-06-11)

problem_statement: Unnecessary `_aliased` suffix added to models generated from GraphQL union types
**Describe the bug**

When using a union in GraphQL, an unnecessary `_aliased` suffix is added.

```python
class GroupMetadata(BaseModel):
    name: String
    typename__: Optional[Literal['GroupMetadata']] = Field(
        'GroupMetadata', alias='__typename'
    )


class Resource(BaseModel):
    metadata: UserMetadata_aliased
    typename__: Optional[Literal['Resource']] = Field('Resource', alias='__typename')


class UserMetadata(BaseModel):
    name: String
    typename__: Optional[Literal['UserMetadata']] = Field(
        'UserMetadata', alias='__typename'
    )


Metadata: TypeAlias = Union[
    'GroupMetadata',
    'UserMetadata',
]
```

**To Reproduce**

Example schema:

```graphql
type GroupMetadata {
    name: String!
}

type UserMetadata {
    name: String!
}

union Metadata = UserMetadata | GroupMetadata

type Resource {
    metadata: UserMetadata!
}
```

Used commandline:
```
$ poetry run datamodel-codegen --input ./test.graphqls --input-file-type graphql --output ./test.py
```

**Expected behavior**

```python
class GroupMetadata(BaseModel):
    name: String
    typename__: Optional[Literal['GroupMetadata']] = Field(
        'GroupMetadata', alias='__typename'
    )


class Resource(BaseModel):
    metadata: UserMetadata
    typename__: Optional[Literal['Resource']] = Field('Resource', alias='__typename')


class UserMetadata(BaseModel):
    name: String
    typename__: Optional[Literal['UserMetadata']] = Field(
        'UserMetadata', alias='__typename'
    )


Metadata: TypeAlias = Union[
    'GroupMetadata',
    'UserMetadata',
]
```

**Version:**
 - OS: MacOS 15.4.1
 - Python version: 3.12.1
 - datamodel-code-generator version: v0.30.0

**Additional context**

I discovered this issue when upgrading datamodel-code-generator from v0.28.5 to v0.30.0.

I confirmed that when the `union Metadata = UserMetadata | GroupMetadata` line is removed from the GraphQL schema, the generated Python code does not include the `_aliased` suffix, as shown below:

```graphql
type GroupMetadata {
    name: String!
}

type UserMetadata {
    name: String!
}

type Resource {
    metadata: UserMetadata!
}
```

date: 2025-06-04
patch: diff --git a/src/datamodel_code_generator/parser/base.py b/src/datamodel_code_generator/parser/base.py
index 74df98e54..098ceea49 100644
--- a/src/datamodel_code_generator/parser/base.py
+++ b/src/datamodel_code_generator/parser/base.py
@@ -1202,7 +1202,10 @@ def __alias_shadowed_imports(  # noqa: PLR6301
     ) -> None:
         for model in models:
             for model_field in model.fields:
-                if model_field.data_type.type in all_model_field_names:
+                if (
+                    model_field.data_type.type in all_model_field_names
+                    and model_field.data_type.type == model_field.name
+                ):
                     alias = model_field.data_type.type + "_aliased"
                     model_field.data_type.type = alias
                     if model_field.data_type.import_:  # pragma: no cover

test_patch: diff --git a/tests/data/expected/parser/graphql/union-aliased-bug.py b/tests/data/expected/parser/graphql/union-aliased-bug.py
new file mode 100644
index 000000000..096b9f9fb
--- /dev/null
+++ b/tests/data/expected/parser/graphql/union-aliased-bug.py
@@ -0,0 +1,45 @@
+# generated by datamodel-codegen:
+#   filename:  union-aliased-bug.graphql
+#   timestamp: 2019-07-26T00:00:00+00:00
+
+from __future__ import annotations
+
+from typing import Literal, Optional, TypeAlias, Union
+
+from pydantic import BaseModel, Field
+
+Boolean: TypeAlias = bool
+"""
+The `Boolean` scalar type represents `true` or `false`.
+"""
+
+
+String: TypeAlias = 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 GroupMetadata(BaseModel):
+    name: String
+    typename__: Optional[Literal['GroupMetadata']] = Field(
+        'GroupMetadata', alias='__typename'
+    )
+
+
+class Resource(BaseModel):
+    metadata: UserMetadata
+    typename__: Optional[Literal['Resource']] = Field('Resource', alias='__typename')
+
+
+class UserMetadata(BaseModel):
+    name: String
+    typename__: Optional[Literal['UserMetadata']] = Field(
+        'UserMetadata', alias='__typename'
+    )
+
+
+Metadata: TypeAlias = Union[
+    'GroupMetadata',
+    'UserMetadata',
+]
diff --git a/tests/data/graphql/union-aliased-bug.graphql b/tests/data/graphql/union-aliased-bug.graphql
new file mode 100644
index 000000000..27812859b
--- /dev/null
+++ b/tests/data/graphql/union-aliased-bug.graphql
@@ -0,0 +1,13 @@
+type GroupMetadata {
+  name: String!
+}
+
+type UserMetadata {
+  name: String!
+}
+
+union Metadata = UserMetadata | GroupMetadata
+
+type Resource {
+  metadata: UserMetadata!
+}
diff --git a/tests/parser/test_graphql.py b/tests/parser/test_graphql.py
index 5d8d5e7b8..f3e6df565 100644
--- a/tests/parser/test_graphql.py
+++ b/tests/parser/test_graphql.py
@@ -27,3 +27,23 @@ def test_graphql_field_enum() -> None:
         ])
         assert return_code == Exit.OK
         assert output_file.read_text() == (EXPECTED_GRAPHQL_PATH / "field-default-enum.py").read_text()
+
+
+@freeze_time("2019-07-26")
+def test_graphql_union_aliased_bug() -> None:
+    with TemporaryDirectory() as output_dir:
+        output_file: Path = Path(output_dir) / "output.py"
+        return_code: Exit = main([
+            "--input",
+            str(GRAPHQL_DATA_PATH / "union-aliased-bug.graphql"),
+            "--output",
+            str(output_file),
+            "--input-file-type",
+            "graphql",
+        ])
+        assert return_code == Exit.OK
+        actual = output_file.read_text().rstrip()
+        expected = (EXPECTED_GRAPHQL_PATH / "union-aliased-bug.py").read_text().rstrip()
+        if actual != expected:
+            pass
+        assert actual == expected


----

problem_statement: Incorrect Literal placeholder generated for Pydantic v2 discriminated union members
**Describe the bug**
When generating Pydantic v2 models from a JSON schema defining a discriminated union (`oneOf` + `discriminator`), `datamodel-codegen` generates incorrect placeholder strings (e.g., `'0#-datamodel-code-generator-#-object-#-special-#'`) within the `typing.Literal` annotations for the discriminator field in the individual member models. It should instead use the `const` value specified in the input schema.

**To Reproduce**

Example schema (`schema.json`):
```json
{
  "title": "Container",
  "type": "object",
  "properties": {
    "items_list": {
      "type": "array",
      "items": {
        "title": "Item",
        "oneOf": [
          {
            "title": "ItemA",
            "type": "object",
            "properties": {
              "item_type": {
                "const": "type_a",
                "type": "string",
                "description": "Discriminator field value for Type A"
              },
              "value_a": {
                "type": "integer",
                 "description": "Specific value for Type A"
              }
            },
            "required": ["item_type", "value_a"]
          },
          {
            "title": "ItemB",
            "type": "object",
            "properties": {
              "item_type": {
                "const": "type_b",
                "type": "string",
                "description": "Discriminator field value for Type B"
              },
              "value_b": {
                "type": "string",
                 "description": "Specific value for Type B"
              }
            },
            "required": ["item_type", "value_b"]
          }
        ],
        "discriminator": {
          "propertyName": "item_type"
        }
      }
    }
  }
}
```

Used commandline:
```bash
$ datamodel-codegen \
    --input schema.json \
    --input-file-type jsonschema \
    --output model.py \
    --output-model-type pydantic_v2.BaseModel \
    --use-annotated \
    --use-standard-collections \
    --field-constraints \
    --target-python-version 3.11
```

**Actual Behavior:** 
The generated `model.py` contains classes `ItemsList1` and `ItemsList2` (derived from the property name `items_list` containing the union) with the `item_type` field defined with incorrect `Literal` placeholders:

```python
# generated by datamodel-codegen:
#   filename:  schema.json
#   timestamp: [Timestamp will vary]

from __future__ import annotations

from typing import Annotated, Literal, Optional, Union

from pydantic import BaseModel, Field, RootModel


class ItemsList1(BaseModel):
    item_type: Annotated[
        # BUG: Incorrect Literal placeholder
        Literal['0#-datamodel-code-generator-#-object-#-special-#'],
        Field(description='Discriminator field value for Type A'),
    ]
    value_a: Annotated[int, Field(description='Specific value for Type A')]


class ItemsList2(BaseModel):
    item_type: Annotated[
        # BUG: Incorrect Literal placeholder
        Literal['1#-datamodel-code-generator-#-object-#-special-#'],
        Field(description='Discriminator field value for Type B'),
    ]
    value_b: Annotated[str, Field(description='Specific value for Type B')]


class ItemsList(RootModel[Union[ItemsList1, ItemsList2]]):
    root: Annotated[
        Union[ItemsList1, ItemsList2], Field(discriminator='item_type', title='Item')
    ]


class Container(BaseModel):
    items_list: Optional[list[ItemsList]] = None
```


**Expected behavior**
The generated Python code for the member models (`ItemsList1`, `ItemsList2`) should contain `typing.Literal` annotations reflecting the actual `const` values specified in the schema (e.g., `'type_a'`, `'type_b'`).

Example of expected code structure:
```python
# [Imports and other parts...]

class ItemsList1(BaseModel):
    item_type: Annotated[
        # EXPECTED: Correct Literal from schema const
        Literal['type_a'],
        Field(description='Discriminator field value for Type A'),
    ]
    value_a: Annotated[int, Field(description='Specific value for Type A')]


class ItemsList2(BaseModel):
    item_type: Annotated[
        # EXPECTED: Correct Literal from schema const
        Literal['type_b'],
        Field(description='Discriminator field value for Type B'),
    ]
    value_b: Annotated[str, Field(description='Specific value for Type B')]

# NOTE: ItemsList and Container definitions expected as generated.
class ItemsList(RootModel[Union[ItemsList1, ItemsList2]]):
    root: Annotated[
        Union[ItemsList1, ItemsList2], Field(discriminator='item_type', title='Item')
    ]

class Container(BaseModel):
    items_list: Optional[list[ItemsList]] = None

# [Rest of the generated code...]
```

**Version:**
 - OS: `macOS Sequoia 15.4.1 `
 - Python version: `3.12.2`
 - datamodel-code-generator version: `0.30` and have tried on `0.30.1.dev3+g4fa8ea3`

**Additional context**
It's worth noting that the `Annotated[Union[...], Field(discriminator='item_type')]` on the `ItemsList` RootModel *is* generated correctly. The issue seems specifically related to resolving the `const` value for the `Literal` annotation within the individual union member classes (`ItemsList1`, `ItemsList2`).

date: 2025-06-07
patch: diff --git a/src/datamodel_code_generator/parser/base.py b/src/datamodel_code_generator/parser/base.py
index a6642ba27..413e56981 100644
--- a/src/datamodel_code_generator/parser/base.py
+++ b/src/datamodel_code_generator/parser/base.py
@@ -772,19 +772,31 @@ def check_paths(
                                     continue
                             type_names.append(name)
 
-                    # Check the main discriminator model path
-                    if mapping:
-                        check_paths(discriminator_model, mapping)  # pyright: ignore[reportArgumentType]
-
-                        # Check the base_classes if they exist
-                        if len(type_names) == 0:
-                            for base_class in discriminator_model.base_classes:
-                                check_paths(base_class.reference, mapping)  # pyright: ignore[reportArgumentType]
-                    else:
-                        type_names = [discriminator_model.path.split("/")[-1]]
+                    # First try to get the discriminator value from the const field
+                    for discriminator_field in discriminator_model.fields:
+                        if field_name not in {discriminator_field.original_name, discriminator_field.name}:
+                            continue
+                        if discriminator_field.extras.get("const"):
+                            type_names = [discriminator_field.extras["const"]]
+                            break
+
+                    # If no const value found, try to get it from the mapping
+                    if not type_names:
+                        # Check the main discriminator model path
+                        if mapping:
+                            check_paths(discriminator_model, mapping)  # pyright: ignore[reportArgumentType]
+
+                            # Check the base_classes if they exist
+                            if len(type_names) == 0:
+                                for base_class in discriminator_model.base_classes:
+                                    check_paths(base_class.reference, mapping)  # pyright: ignore[reportArgumentType]
+                        else:
+                            type_names = [discriminator_model.path.split("/")[-1]]
+
                     if not type_names:  # pragma: no cover
                         msg = f"Discriminator type is not found. {data_type.reference.path}"
                         raise RuntimeError(msg)
+
                     has_one_literal = False
                     for discriminator_field in discriminator_model.fields:
                         if field_name not in {discriminator_field.original_name, discriminator_field.name}:

test_patch: diff --git a/tests/data/expected/main/jsonschema/discriminator_no_mapping.py b/tests/data/expected/main/jsonschema/discriminator_no_mapping.py
new file mode 100644
index 000000000..5ca009df7
--- /dev/null
+++ b/tests/data/expected/main/jsonschema/discriminator_no_mapping.py
@@ -0,0 +1,21 @@
+# generated by datamodel-codegen:
+#   filename:  discriminator_no_mapping.json
+#   timestamp: 2019-07-26T00:00:00+00:00
+
+from __future__ import annotations
+
+from typing import Literal, Union
+
+from pydantic import BaseModel, Field
+
+
+class Cat(BaseModel):
+    pet_type: Literal['cat']
+
+
+class Dog(BaseModel):
+    pet_type: Literal['dog']
+
+
+class Animal(BaseModel):
+    pet: Union[Cat, Dog] = Field(..., discriminator='pet_type', title='Pet')
diff --git a/tests/data/jsonschema/discriminator_no_mapping.json b/tests/data/jsonschema/discriminator_no_mapping.json
new file mode 100644
index 000000000..e641defdf
--- /dev/null
+++ b/tests/data/jsonschema/discriminator_no_mapping.json
@@ -0,0 +1,47 @@
+{
+  "$defs": {
+    "Cat": {
+      "properties": {
+        "pet_type": {
+          "const": "cat"
+        }
+      },
+      "required": [
+        "pet_type"
+      ],
+      "type": "object"
+    },
+    "Dog": {
+      "properties": {
+        "pet_type": {
+          "const": "dog"
+        }
+      },
+      "required": [
+        "pet_type"
+      ],
+      "type": "object"
+    }
+  },
+  "properties": {
+    "pet": {
+      "discriminator": {
+        "propertyName": "pet_type"
+      },
+      "oneOf": [
+        {
+          "$ref": "#/$defs/Cat"
+        },
+        {
+          "$ref": "#/$defs/Dog"
+        }
+      ],
+      "title": "Pet"
+    }
+  },
+  "required": [
+    "pet"
+  ],
+  "title": "Animal",
+  "type": "object"
+} 
\ No newline at end of file
diff --git a/tests/main/jsonschema/test_main_jsonschema.py b/tests/main/jsonschema/test_main_jsonschema.py
index 68a57c36d..2e8a21ad4 100644
--- a/tests/main/jsonschema/test_main_jsonschema.py
+++ b/tests/main/jsonschema/test_main_jsonschema.py
@@ -2657,6 +2657,28 @@ def test_main_jsonschema_discriminator_literals(output_model: str, expected_outp
         assert output_file.read_text() == (EXPECTED_JSON_SCHEMA_PATH / expected_output).read_text()
 
 
+@freeze_time("2019-07-26")
+@pytest.mark.skipif(
+    int(black.__version__.split(".")[0]) < 24,
+    reason="Installed black doesn't support the new style",
+)
+def test_main_jsonschema_discriminator_literals_with_no_mapping(min_version: str) -> None:
+    with TemporaryDirectory() as output_dir:
+        output_file: Path = Path(output_dir) / "output.py"
+        return_code: Exit = main([
+            "--input",
+            str(JSON_SCHEMA_DATA_PATH / "discriminator_no_mapping.json"),
+            "--output",
+            str(output_file),
+            "--output-model-type",
+            "pydantic_v2.BaseModel",
+            "--target-python",
+            min_version,
+        ])
+        assert return_code == Exit.OK
+        assert output_file.read_text() == (EXPECTED_JSON_SCHEMA_PATH / "discriminator_no_mapping.py").read_text()
+
+
 @pytest.mark.parametrize(
     ("output_model", "expected_output"),
     [


----

problem_statement: capitalize-enum-member not working with use-subclass-enum  and snake-case-field when typed enum
              the problem is still there for 0.30.1 when using `--use-subclass-enum  --capitalize-enum-member  --snake-case-field` 
the fields are not capitalized when both subclass and snake case are used.

_Originally posted by @ant31 in https://github.com/koxudaxi/datamodel-code-generator/issues/2389#issuecomment-2842568539_
            

date: 2025-06-11
patch: diff --git a/src/datamodel_code_generator/parser/base.py b/src/datamodel_code_generator/parser/base.py
index 413e56981..cd32c1517 100644
--- a/src/datamodel_code_generator/parser/base.py
+++ b/src/datamodel_code_generator/parser/base.py
@@ -1135,7 +1135,7 @@ def __change_field_name(
         if self.data_model_type != pydantic_model_v2.BaseModel:
             return
         for model in models:
-            if model.base_class == "Enum":
+            if "Enum" in model.base_class:
                 continue
 
             for field in model.fields:

test_patch: diff --git a/tests/test_main_kr.py b/tests/test_main_kr.py
index 197f3c015..a5a42749b 100644
--- a/tests/test_main_kr.py
+++ b/tests/test_main_kr.py
@@ -362,3 +362,62 @@ class EnumSystems(Enum):
     assert output_file_read_text == expected_output, (
         f"\nExpected  output:\n{expected_output}\n\nGenerated output:\n{output_file_read_text}"
     )
+
+
+def test_capitalise_enum_members_and_use_subclass_enum(tmp_path: Path) -> None:
+    """Combination of capitalise-enum-members and use-subclass-enum not working since v0.28.5
+
+    From https://github.com/koxudaxi/datamodel-code-generator/issues/2395
+    """
+    input_data = """
+openapi: 3.0.3
+info:
+  version: X.Y.Z
+  title: example schema
+servers:
+  - url: "https://acme.org"
+paths: {}
+components:
+  schemas:
+    EnumSystems:
+      type: string
+      enum:
+        - linux
+        - osx
+        - windows
+"""
+    input_file = tmp_path / "myschema.yaml"
+    input_file.write_text(input_data, encoding="utf_8")
+
+    expected_output = """# generated by datamodel-codegen:
+#   filename:  myschema.yaml
+
+from __future__ import annotations
+
+from enum import Enum
+
+
+class EnumSystems(str, Enum):
+    LINUX = 'linux'
+    OSX = 'osx'
+    WINDOWS = 'windows'
+"""
+
+    output_file: Path = tmp_path / "output.py"
+    return_code: Exit = main([
+        "--output-model-type",
+        "pydantic_v2.BaseModel",
+        "--disable-timestamp",
+        "--capitalise-enum-members",
+        "--snake-case-field",
+        "--use-subclass-enum",
+        "--input",
+        str(input_file),
+        "--output",
+        str(output_file),
+    ])
+    assert return_code == Exit.OK
+    output_file_read_text = output_file.read_text(encoding="utf_8")
+    assert output_file_read_text == expected_output, (
+        f"\nExpected  output:\n{expected_output}\n\nGenerated output:\n{output_file_read_text}"
+    )