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
4 changes: 3 additions & 1 deletion docs/cli-reference/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ This documentation is auto-generated from test cases.
| 📁 [Base Options](base-options.md) | 5 | Input/output configuration |
| 🔧 [Typing Customization](typing-customization.md) | 22 | Type annotation and import behavior |
| 🏷️ [Field Customization](field-customization.md) | 21 | Field naming and docstring behavior |
| 🏗️ [Model Customization](model-customization.md) | 31 | Model generation behavior |
| 🏗️ [Model Customization](model-customization.md) | 33 | Model generation behavior |
| 🎨 [Template Customization](template-customization.md) | 17 | Output formatting and custom rendering |
| 📘 [OpenAPI-only Options](openapi-only-options.md) | 6 | OpenAPI-specific features |
| ⚙️ [General Options](general-options.md) | 14 | Utilities and meta options |
Expand Down Expand Up @@ -59,6 +59,7 @@ This documentation is auto-generated from test cases.
- [`--disable-future-imports`](typing-customization.md#disable-future-imports)
- [`--disable-timestamp`](template-customization.md#disable-timestamp)
- [`--disable-warnings`](general-options.md#disable-warnings)
- [`--duplicate-name-suffix`](model-customization.md#duplicate-name-suffix)

### E {#e}

Expand Down Expand Up @@ -113,6 +114,7 @@ This documentation is auto-generated from test cases.

### N {#n}

- [`--naming-strategy`](model-customization.md#naming-strategy)
- [`--no-alias`](field-customization.md#no-alias)
- [`--no-color`](utility-options.md#no-color)
- [`--no-treat-dot-as-module`](template-customization.md#no-treat-dot-as-module)
Expand Down
181 changes: 181 additions & 0 deletions docs/cli-reference/model-customization.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,13 @@
| [`--collapse-reuse-models`](#collapse-reuse-models) | Collapse duplicate models by replacing references instead of... |
| [`--collapse-root-models`](#collapse-root-models) | Inline root model definitions instead of creating separate w... |
| [`--dataclass-arguments`](#dataclass-arguments) | Customize dataclass decorator arguments via JSON dictionary.... |
| [`--duplicate-name-suffix`](#duplicate-name-suffix) | Customize suffix for duplicate model names. |
| [`--enable-faux-immutability`](#enable-faux-immutability) | Enable faux immutability in Pydantic v1 models (allow_mutati... |
| [`--force-optional`](#force-optional) | Force all fields to be Optional regardless of required statu... |
| [`--frozen-dataclasses`](#frozen-dataclasses) | Generate frozen dataclasses with optional keyword-only field... |
| [`--keep-model-order`](#keep-model-order) | Keep model definition order as specified in schema. |
| [`--keyword-only`](#keyword-only) | Generate dataclasses with keyword-only fields (Python 3.10+)... |
| [`--naming-strategy`](#naming-strategy) | Use parent-prefixed naming strategy for duplicate model name... |
| [`--output-model-type`](#output-model-type) | Select the output model type (Pydantic v1/v2, dataclasses, T... |
| [`--parent-scoped-naming`](#parent-scoped-naming) | Namespace models by their parent scope to avoid naming confl... |
| [`--reuse-model`](#reuse-model) | Reuse identical model definitions instead of generating dupl... |
Expand Down Expand Up @@ -1840,6 +1842,95 @@ control over dataclass generation.

---

## `--duplicate-name-suffix` {#duplicate-name-suffix}

Customize suffix for duplicate model names.

The `--duplicate-name-suffix` flag allows specifying custom suffixes for
resolving duplicate names by type. The value is a JSON mapping where keys
are type names ('model', 'enum', 'default') and values are suffix strings.
For example, `{"model": "Schema"}` changes `Item1` to `ItemSchema`.

**Related:** [`--naming-strategy`](model-customization.md#naming-strategy)

!!! tip "Usage"

```bash
datamodel-codegen --input schema.json --duplicate-name-suffix "{"model": "Schema"}" # (1)!
```

1. :material-arrow-left: `--duplicate-name-suffix` - the option documented here

??? example "Examples"

**Input Schema:**

```json
{
"$schema": "http://json-schema.org/draft-07/schema#",
"definitions": {
"Order": {
"type": "object",
"properties": {
"item": {
"type": "object",
"properties": {
"name": {"type": "string"}
}
}
}
},
"Cart": {
"type": "object",
"properties": {
"item": {
"type": "object",
"properties": {
"quantity": {"type": "integer"}
}
}
}
}
}
}
```

**Output:**

```python
# generated by datamodel-codegen:
# filename: input.json
# timestamp: 2019-07-26T00:00:00+00:00

from __future__ import annotations

from typing import Any

from pydantic import BaseModel, RootModel


class Model(RootModel[Any]):
root: Any


class Item(BaseModel):
name: str | None = None


class Order(BaseModel):
item: Item | None = None


class ItemSchema(BaseModel):
quantity: int | None = None


class Cart(BaseModel):
item: ItemSchema | None = None
```

---

## `--enable-faux-immutability` {#enable-faux-immutability}

Enable faux immutability in Pydantic v1 models (allow_mutation=False).
Expand Down Expand Up @@ -3168,6 +3259,94 @@ positional argument errors.

---

## `--naming-strategy` {#naming-strategy}

Use parent-prefixed naming strategy for duplicate model names.

The `--naming-strategy parent-prefixed` flag prefixes model names with their
parent model name when duplicates occur. For example, if both `Order` and
`Cart` have an inline `Item` definition, they become `OrderItem` and `CartItem`.

**Related:** [`--duplicate-name-suffix`](model-customization.md#duplicate-name-suffix), [`--parent-scoped-naming`](model-customization.md#parent-scoped-naming)

!!! tip "Usage"

```bash
datamodel-codegen --input schema.json --naming-strategy parent-prefixed # (1)!
```

1. :material-arrow-left: `--naming-strategy` - the option documented here

??? example "Examples"

**Input Schema:**

```json
{
"$schema": "http://json-schema.org/draft-07/schema#",
"definitions": {
"Order": {
"type": "object",
"properties": {
"item": {
"type": "object",
"properties": {
"name": {"type": "string"}
}
}
}
},
"Cart": {
"type": "object",
"properties": {
"item": {
"type": "object",
"properties": {
"quantity": {"type": "integer"}
}
}
}
}
}
}
```

**Output:**

```python
# generated by datamodel-codegen:
# filename: input.json
# timestamp: 2019-07-26T00:00:00+00:00

from __future__ import annotations

from typing import Any

from pydantic import BaseModel, RootModel


class Model(RootModel[Any]):
root: Any


class ModelOrderItem(BaseModel):
name: str | None = None


class ModelOrder(BaseModel):
item: ModelOrderItem | None = None


class ModelCartItem(BaseModel):
quantity: int | None = None


class ModelCart(BaseModel):
item: ModelCartItem | None = None
```

---

## `--output-model-type` {#output-model-type}

Select the output model type (Pydantic v1/v2, dataclasses, TypedDict, msgspec).
Expand Down Expand Up @@ -3756,6 +3935,8 @@ The `--parent-scoped-naming` flag prefixes model names with their parent scope
(operation/path/parameter) to prevent name collisions when the same model name
appears in different contexts within an OpenAPI specification.

**Deprecated:** Use --naming-strategy parent-prefixed instead.

!!! tip "Usage"

```bash
Expand Down
4 changes: 4 additions & 0 deletions docs/cli-reference/quick-reference.md
Original file line number Diff line number Diff line change
Expand Up @@ -87,11 +87,13 @@ datamodel-codegen [OPTIONS]
| [`--collapse-reuse-models`](model-customization.md#collapse-reuse-models) | Collapse duplicate models by replacing references instead of inheritance. |
| [`--collapse-root-models`](model-customization.md#collapse-root-models) | Inline root model definitions instead of creating separate wrapper classes. |
| [`--dataclass-arguments`](model-customization.md#dataclass-arguments) | Customize dataclass decorator arguments via JSON dictionary. |
| [`--duplicate-name-suffix`](model-customization.md#duplicate-name-suffix) | Customize suffix for duplicate model names. |
| [`--enable-faux-immutability`](model-customization.md#enable-faux-immutability) | Enable faux immutability in Pydantic v1 models (allow_mutation=False). |
| [`--force-optional`](model-customization.md#force-optional) | Force all fields to be Optional regardless of required status. |
| [`--frozen-dataclasses`](model-customization.md#frozen-dataclasses) | Generate frozen dataclasses with optional keyword-only fields. |
| [`--keep-model-order`](model-customization.md#keep-model-order) | Keep model definition order as specified in schema. |
| [`--keyword-only`](model-customization.md#keyword-only) | Generate dataclasses with keyword-only fields (Python 3.10+). |
| [`--naming-strategy`](model-customization.md#naming-strategy) | Use parent-prefixed naming strategy for duplicate model names. |
| [`--output-model-type`](model-customization.md#output-model-type) | Select the output model type (Pydantic v1/v2, dataclasses, TypedDict, msgspec). |
| [`--parent-scoped-naming`](model-customization.md#parent-scoped-naming) | Namespace models by their parent scope to avoid naming conflicts. |
| [`--reuse-model`](model-customization.md#reuse-model) | Reuse identical model definitions instead of generating duplicates. |
Expand Down Expand Up @@ -205,6 +207,7 @@ All options sorted alphabetically:
- [`--disable-future-imports`](typing-customization.md#disable-future-imports) - Prevent automatic addition of __future__ imports in generate...
- [`--disable-timestamp`](template-customization.md#disable-timestamp) - Disable timestamp in generated file header for reproducible ...
- [`--disable-warnings`](general-options.md#disable-warnings) - Suppress warning messages during code generation.
- [`--duplicate-name-suffix`](model-customization.md#duplicate-name-suffix) - Customize suffix for duplicate model names.
- [`--empty-enum-field-name`](field-customization.md#empty-enum-field-name) - Name for empty string enum field values.
- [`--enable-command-header`](template-customization.md#enable-command-header) - Include command-line options in file header for reproducibil...
- [`--enable-faux-immutability`](model-customization.md#enable-faux-immutability) - Enable faux immutability in Pydantic v1 models (allow_mutati...
Expand Down Expand Up @@ -235,6 +238,7 @@ All options sorted alphabetically:
- [`--keep-model-order`](model-customization.md#keep-model-order) - Keep model definition order as specified in schema.
- [`--keyword-only`](model-customization.md#keyword-only) - Generate dataclasses with keyword-only fields (Python 3.10+)...
- [`--module-split-mode`](general-options.md#module-split-mode) - Split generated models into separate files, one per model cl...
- [`--naming-strategy`](model-customization.md#naming-strategy) - Use parent-prefixed naming strategy for duplicate model name...
- [`--no-alias`](field-customization.md#no-alias) - Disable Field alias generation for non-Python-safe property ...
- [`--no-color`](utility-options.md#no-color) - Disable colorized output
- [`--no-treat-dot-as-module`](template-customization.md#no-treat-dot-as-module) - Keep dots in schema names as underscores for flat output.
Expand Down
1 change: 1 addition & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -212,6 +212,7 @@ skip = '.git,*.lock,tests,docs/cli-reference,CHANGELOG.md,docs/changelog.md'
filterwarnings = [
"error",
"ignore:^.*`--validation` option is deprecated.*",
"ignore:^.*--parent-scoped-naming is deprecated.*",
"ignore:^.*Field name `name` is duplicated on Pet.*",
"ignore:^.*format of 'unknown-type' not understood for 'string' - using default.*",
"ignore:^.*unclosed file.*",
Expand Down
20 changes: 20 additions & 0 deletions src/datamodel_code_generator/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -283,6 +283,21 @@ class FieldTypeCollisionStrategy(Enum):
RenameType = "rename-type"


class NamingStrategy(Enum):
"""Strategy for generating unique model names when duplicates occur.

numbered: Append numeric suffix (Address1, Address2) [default].
parent_prefixed: Prefix with parent model name (CustomerAddress, UserAddress).
full_path: Use full schema path for unique names (OrdersItemsAddress).
primary_first: Prioritize primary schema definitions, others get suffix.
"""

Numbered = "numbered"
ParentPrefixed = "parent-prefixed"
FullPath = "full-path"
PrimaryFirst = "primary-first"


class AllOfMergeMode(Enum):
"""Mode for field merging in allOf schemas.

Expand Down Expand Up @@ -508,6 +523,8 @@ def generate( # noqa: PLR0912, PLR0913, PLR0914, PLR0915
formatters: list[Formatter] = DEFAULT_FORMATTERS,
settings_path: Path | None = None,
parent_scoped_naming: bool = False,
naming_strategy: NamingStrategy | None = None,
duplicate_name_suffix: dict[str, str] | None = None,
dataclass_arguments: DataclassArguments | None = None,
disable_future_imports: bool = False,
type_mappings: list[str] | None = None,
Expand Down Expand Up @@ -770,6 +787,8 @@ def get_header_and_first_line(csv_file: IO[str]) -> dict[str, Any]:
formatters=formatters,
encoding=encoding,
parent_scoped_naming=parent_scoped_naming,
naming_strategy=naming_strategy,
duplicate_name_suffix=duplicate_name_suffix,
dataclass_arguments=dataclass_arguments,
type_mappings=type_mappings,
type_overrides=type_overrides,
Expand Down Expand Up @@ -927,6 +946,7 @@ def infer_input_type(text: str) -> InputFileType:
"InvalidClassNameError",
"LiteralType",
"ModuleSplitMode",
"NamingStrategy",
"PythonVersion",
"ReadOnlyWriteOnlyModelType",
"TargetPydanticVersion",
Expand Down
29 changes: 29 additions & 0 deletions src/datamodel_code_generator/__main__.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
InputFileType,
InvalidClassNameError,
ModuleSplitMode,
NamingStrategy,
OpenAPIScope,
ReadOnlyWriteOnlyModelType,
ReuseScope,
Expand Down Expand Up @@ -238,6 +239,30 @@ def validate_custom_formatters(cls, values: dict[str, Any]) -> dict[str, Any]:
values["custom_formatters"] = custom_formatters.split(",")
return values

@model_validator(mode="before")
def validate_duplicate_name_suffix(cls, values: dict[str, Any]) -> dict[str, Any]: # noqa: N805
Comment thread Dismissed
"""Validate and parse duplicate_name_suffix JSON string."""
duplicate_name_suffix = values.get("duplicate_name_suffix")
if duplicate_name_suffix is not None and isinstance(duplicate_name_suffix, str):
try:
values["duplicate_name_suffix"] = json.loads(duplicate_name_suffix)
except json.JSONDecodeError as e:
msg = f"Invalid JSON for --duplicate-name-suffix: {e}"
raise Error(msg) from e
return values

@model_validator(mode="before")
def validate_naming_strategy_migration(cls, values: dict[str, Any]) -> dict[str, Any]: # noqa: N805
Comment thread Dismissed
"""Migrate deprecated --parent-scoped-naming to --naming-strategy."""
if values.get("parent_scoped_naming") and not values.get("naming_strategy"):
values["naming_strategy"] = NamingStrategy.ParentPrefixed
warnings.warn(
"--parent-scoped-naming is deprecated. Use --naming-strategy parent-prefixed instead.",
DeprecationWarning,
stacklevel=2,
)
return values

@model_validator(mode="before")
def validate_class_decorators(cls, values: dict[str, Any]) -> dict[str, Any]: # noqa: N805
"""Validate and split class decorators, adding @ prefix if missing."""
Expand Down Expand Up @@ -489,6 +514,8 @@ def validate_all_exports_collision_strategy(cls, values: dict[str, Any]) -> dict
use_default_factory_for_optional_nested_models: bool = False
formatters: list[Formatter] = DEFAULT_FORMATTERS
parent_scoped_naming: bool = False
naming_strategy: Optional[NamingStrategy] = None # noqa: UP045
duplicate_name_suffix: Optional[dict[str, str]] = None # noqa: UP045
disable_future_imports: bool = False
type_mappings: Optional[list[str]] = None # noqa: UP045
type_overrides: Optional[dict[str, str]] = None # noqa: UP045
Expand Down Expand Up @@ -804,6 +831,8 @@ def run_generate_from_config( # noqa: PLR0913, PLR0917
formatters=config.formatters,
settings_path=settings_path,
parent_scoped_naming=config.parent_scoped_naming,
naming_strategy=config.naming_strategy,
duplicate_name_suffix=config.duplicate_name_suffix,
dataclass_arguments=config.dataclass_arguments,
disable_future_imports=config.disable_future_imports,
type_mappings=config.type_mappings,
Expand Down
Loading
Loading