Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
92 changes: 92 additions & 0 deletions docs/cli-reference/graphql-only-options.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
# 📋 GraphQL-only Options

## 📋 Options

| Option | Description |
|--------|-------------|
| [`--graphql-no-typename`](#graphql-no-typename) | Exclude __typename field from generated GraphQL models. |

---

## `--graphql-no-typename` {#graphql-no-typename}

Exclude __typename field from generated GraphQL models.

The `--graphql-no-typename` flag prevents the generator from adding the
`typename__` field (aliased to `__typename`) to generated models. This is
useful when using generated models for GraphQL mutations, as servers typically
don't expect this field in input data.

!!! tip "Usage"

```bash
datamodel-codegen --input schema.json --graphql-no-typename # (1)!
```

1. :material-arrow-left: `--graphql-no-typename` - the option documented here

??? example "Examples"

**Input Schema:**

```graphql
type Book {
id: ID!
title: String
}

interface Node {
id: ID!
}

input BookInput {
title: String!
}
```

**Output:**

```python
# generated by datamodel-codegen:
# filename: no-typename.graphql
# timestamp: 2019-07-26T00:00:00+00:00

from __future__ import annotations

from typing import TypeAlias

from pydantic import BaseModel

Boolean: TypeAlias = bool
"""
The `Boolean` scalar type represents `true` or `false`.
"""


ID: TypeAlias = 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.
"""


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 Node(BaseModel):
id: ID


class Book(BaseModel):
id: ID
title: String | None = None


class BookInput(BaseModel):
title: String
```

---

2 changes: 2 additions & 0 deletions docs/cli-reference/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ This documentation is auto-generated from test cases.
| 🏗️ [Model Customization](model-customization.md) | 39 | Model generation behavior |
| 🎨 [Template Customization](template-customization.md) | 18 | Output formatting and custom rendering |
| 📘 [OpenAPI-only Options](openapi-only-options.md) | 7 | OpenAPI-specific features |
| 📋 [GraphQL-only Options](graphql-only-options.md) | 1 | |
| ⚙️ [General Options](general-options.md) | 15 | Utilities and meta options |
| 📝 [Utility Options](utility-options.md) | 6 | Help, version, debug options |

Expand Down Expand Up @@ -94,6 +95,7 @@ This documentation is auto-generated from test cases.
- [`--generate-cli-command`](general-options.md#generate-cli-command)
- [`--generate-prompt`](utility-options.md#generate-prompt)
- [`--generate-pyproject-config`](general-options.md#generate-pyproject-config)
- [`--graphql-no-typename`](graphql-only-options.md#graphql-no-typename)

### H {#h}

Expand Down
7 changes: 7 additions & 0 deletions docs/cli-reference/quick-reference.md
Original file line number Diff line number Diff line change
Expand Up @@ -162,6 +162,12 @@ datamodel-codegen [OPTIONS]
| [`--use-status-code-in-response-name`](openapi-only-options.md#use-status-code-in-response-name) | Include HTTP status code in response model names. |
| [`--validation`](openapi-only-options.md#validation) | Enable validation constraints (deprecated, use --field-constraints). |

### 📋 GraphQL-only Options

| Option | Description |
|--------|-------------|
| [`--graphql-no-typename`](graphql-only-options.md#graphql-no-typename) | Exclude __typename field from generated GraphQL models. |

### ⚙️ General Options

| Option | Description |
Expand Down Expand Up @@ -251,6 +257,7 @@ All options sorted alphabetically:
- [`--generate-cli-command`](general-options.md#generate-cli-command) - Generate CLI command from pyproject.toml configuration.
- [`--generate-prompt`](utility-options.md#generate-prompt) - Generate a prompt for consulting LLMs about CLI options
- [`--generate-pyproject-config`](general-options.md#generate-pyproject-config) - Generate pyproject.toml configuration from CLI arguments.
- [`--graphql-no-typename`](graphql-only-options.md#graphql-no-typename) - Exclude __typename field from generated GraphQL models.
- [`--help`](utility-options.md#help) - Show help message and exit
- [`--http-headers`](general-options.md#http-headers) - Fetch schema from URL with custom HTTP headers.
- [`--http-ignore-tls`](general-options.md#http-ignore-tls) - Disable TLS certificate verification for HTTPS requests.
Expand Down
33 changes: 33 additions & 0 deletions docs/graphql.md
Original file line number Diff line number Diff line change
Expand Up @@ -207,6 +207,39 @@ class A(BaseModel):

---

## 🚫 Excluding __typename Field

When using generated models for GraphQL mutations, the `__typename` field may cause issues
as GraphQL servers typically don't expect this field in input data.

Use the `--graphql-no-typename` option to exclude this field:

```bash
datamodel-codegen --input schema.graphql --input-file-type graphql --output model.py --graphql-no-typename
```

**Before (default):**
```python
class Book(BaseModel):
id: ID
title: String | None = None
typename__: Literal['Book'] | None = Field('Book', alias='__typename')
```

**After (with --graphql-no-typename):**
```python
class Book(BaseModel):
id: ID
title: String | None = None
```

!!! warning "Union Type Discrimination"
If your schema uses GraphQL union types and you rely on `__typename` for type
discrimination during deserialization, excluding this field may break that functionality.
Consider using this option only for input types or schemas without unions.

---

## 📖 See Also

- 🖥️ [CLI Reference](cli-reference/index.md) - Complete CLI options reference
Expand Down
5 changes: 5 additions & 0 deletions src/datamodel_code_generator/__main__.py
Original file line number Diff line number Diff line change
Expand Up @@ -551,6 +551,7 @@ def validate_class_name_affix_scope(cls, v: str | ClassNameAffixScope | None) ->
openapi_scopes: Optional[list[OpenAPIScope]] = [OpenAPIScope.Schemas] # noqa: UP045
include_path_parameters: bool = False
openapi_include_paths: Optional[list[str]] = None # noqa: UP045
graphql_no_typename: bool = False
wrap_string_literal: Optional[bool] = None # noqa: UP045
use_title_as_name: bool = False
use_operation_id_as_name: bool = False
Expand Down Expand Up @@ -929,6 +930,7 @@ def run_generate_from_config( # noqa: PLR0913, PLR0917
openapi_scopes=config.openapi_scopes,
include_path_parameters=config.include_path_parameters,
openapi_include_paths=config.openapi_include_paths,
graphql_no_typename=config.graphql_no_typename,
wrap_string_literal=config.wrap_string_literal,
use_title_as_name=config.use_title_as_name,
use_operation_id_as_name=config.use_operation_id_as_name,
Expand Down Expand Up @@ -1000,6 +1002,9 @@ def run_generate_from_config( # noqa: PLR0913, PLR0917

def main(args: Sequence[str] | None = None) -> Exit: # noqa: PLR0911, PLR0912, PLR0914, PLR0915
"""Execute datamodel code generation from command-line arguments."""
vars(namespace).clear()
namespace.no_color = False

if "_ARGCOMPLETE" in os.environ: # pragma: no cover
import argcomplete # noqa: PLC0415

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,7 @@ class GenerateConfigDict(TypedDict):
include_path_parameters: NotRequired[bool]
openapi_include_paths: NotRequired[list[str] | None]
graphql_scopes: NotRequired[list[GraphQLScope] | None]
graphql_no_typename: NotRequired[bool]
wrap_string_literal: NotRequired[bool | None]
use_title_as_name: NotRequired[bool]
use_operation_id_as_name: NotRequired[bool]
Expand Down
1 change: 1 addition & 0 deletions src/datamodel_code_generator/_types/parser_config_dicts.py
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,7 @@ class ParserConfigDict(TypedDict):
class GraphQLParserConfigDict(ParserConfigDict):
data_model_scalar_type: NotRequired[type[DataModel]]
data_model_union_type: NotRequired[type[DataModel]]
graphql_no_typename: NotRequired[bool]


class JSONSchemaParserConfigDict(ParserConfigDict):
Expand Down
12 changes: 12 additions & 0 deletions src/datamodel_code_generator/arguments.py
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,7 @@ def start_section(self, heading: str | None) -> None:
extra_fields_model_options = model_options.add_mutually_exclusive_group()
template_options = arg_parser.add_argument_group("Template customization")
openapi_options = arg_parser.add_argument_group("OpenAPI-only options")
graphql_options = arg_parser.add_argument_group("GraphQL-only options")
general_options = arg_parser.add_argument_group("General options")

# ======================================================================================
Expand Down Expand Up @@ -958,6 +959,17 @@ def start_section(self, heading: str | None) -> None:
default=None,
)

# ======================================================================================
# Options specific to GraphQL input schemas
# ======================================================================================
graphql_options.add_argument(
"--graphql-no-typename",
help="Exclude __typename field from generated GraphQL models. "
"Useful when using generated models for GraphQL mutations.",
action="store_true",
default=None,
)

# ======================================================================================
# General options
# ======================================================================================
Expand Down
5 changes: 5 additions & 0 deletions src/datamodel_code_generator/cli_options.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ class OptionCategory(str, Enum):
MODEL = "Model Customization"
TEMPLATE = "Template Customization"
OPENAPI = "OpenAPI-only Options"
GRAPHQL = "GraphQL-only Options"
GENERAL = "General Options"


Expand Down Expand Up @@ -244,6 +245,10 @@ class CLIOptionMeta:
deprecated_message="Use --field-constraints instead",
),
# ==========================================================================
# GraphQL-only Options
# ==========================================================================
"--graphql-no-typename": CLIOptionMeta(name="--graphql-no-typename", category=OptionCategory.GRAPHQL),
# ==========================================================================
# General Options
# ==========================================================================
"--check": CLIOptionMeta(name="--check", category=OptionCategory.GENERAL),
Expand Down
2 changes: 2 additions & 0 deletions src/datamodel_code_generator/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,7 @@ class Config:
include_path_parameters: bool = False
openapi_include_paths: list[str] | None = None
graphql_scopes: list[GraphQLScope] | None = None
graphql_no_typename: bool = False
wrap_string_literal: bool | None = None
use_title_as_name: bool = False
use_operation_id_as_name: bool = False
Expand Down Expand Up @@ -336,6 +337,7 @@ class GraphQLParserConfig(ParserConfig):

data_model_scalar_type: type[DataModel] = DataTypeScalar
data_model_union_type: type[DataModel] = DataTypeUnion
graphql_no_typename: bool = False


class JSONSchemaParserConfig(ParserConfig):
Expand Down
3 changes: 2 additions & 1 deletion src/datamodel_code_generator/parser/graphql.py
Original file line number Diff line number Diff line change
Expand Up @@ -425,7 +425,8 @@ def parse_object_like(
data_model_field_type = self.parse_field(field_name_, alias, field)
fields.append(data_model_field_type)

fields.append(self._typename_field(obj.name))
if not self.config.graphql_no_typename:
fields.append(self._typename_field(obj.name))

base_classes = []
if hasattr(obj, "interfaces"): # pragma: no cover
Expand Down
1 change: 1 addition & 0 deletions src/datamodel_code_generator/prompt_data.py
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@
"--frozen-dataclasses": "Generate frozen dataclasses with optional keyword-only fields.",
"--generate-cli-command": "Generate CLI command from pyproject.toml configuration.",
"--generate-pyproject-config": "Generate pyproject.toml configuration from CLI arguments.",
"--graphql-no-typename": "Exclude __typename field from generated GraphQL models.",
"--http-headers": "Fetch schema from URL with custom HTTP headers.",
"--http-ignore-tls": "Disable TLS certificate verification for HTTPS requests.",
"--http-query-parameters": "Add query parameters to HTTP requests for remote schemas.",
Expand Down
39 changes: 39 additions & 0 deletions tests/data/expected/main/graphql/no_typename.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
# generated by datamodel-codegen:
# filename: no-typename.graphql
# timestamp: 2019-07-26T00:00:00+00:00

from __future__ import annotations

from typing import TypeAlias

from pydantic import BaseModel

Boolean: TypeAlias = bool
"""
The `Boolean` scalar type represents `true` or `false`.
"""


ID: TypeAlias = 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.
"""


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 Node(BaseModel):
id: ID


class Book(BaseModel):
id: ID
title: String | None = None


class BookInput(BaseModel):
title: String
1 change: 1 addition & 0 deletions tests/data/expected/main/input_model/config_class.py
Original file line number Diff line number Diff line change
Expand Up @@ -177,6 +177,7 @@ class GenerateConfig(TypedDict):
include_path_parameters: NotRequired[bool]
openapi_include_paths: NotRequired[list[str] | None]
graphql_scopes: NotRequired[list[GraphQLScope] | None]
graphql_no_typename: NotRequired[bool]
wrap_string_literal: NotRequired[bool | None]
use_title_as_name: NotRequired[bool]
use_operation_id_as_name: NotRequired[bool]
Expand Down
39 changes: 39 additions & 0 deletions tests/data/expected/parser/graphql/no_typename.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
# generated by datamodel-codegen:
# filename: no-typename.graphql
# timestamp: 2019-07-26T00:00:00+00:00

from __future__ import annotations

from typing import TypeAlias

from pydantic import BaseModel

Boolean: TypeAlias = bool
"""
The `Boolean` scalar type represents `true` or `false`.
"""


ID: TypeAlias = 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.
"""


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 Node(BaseModel):
id: ID


class Book(BaseModel):
id: ID
title: String | None = None


class BookInput(BaseModel):
title: String
12 changes: 12 additions & 0 deletions tests/data/graphql/no-typename.graphql
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
type Book {
id: ID!
title: String
}

interface Node {
id: ID!
}

input BookInput {
title: String!
}
Loading
Loading