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
15 changes: 14 additions & 1 deletion docs/cli-reference/model-customization.md
Original file line number Diff line number Diff line change
Expand Up @@ -1125,7 +1125,20 @@ The `--base-class` flag configures the code generation behavior.
Specify different base classes for specific models via JSON mapping.

The `--base-class-map` option allows you to assign different base classes
to specific models. Priority: base-class-map > customBasePath > base-class.
to specific models. This is useful when you want selective base class inheritance,
for example, applying custom base classes only to specific models while leaving
others with the default `BaseModel`.

Priority: `--base-class-map` > `customBasePath` (schema extension) > `--base-class`

You can specify either a single base class as a string, or multiple base classes
(mixins) as a list:

- Single: `{"Person": "custom.bases.PersonBase"}`
- Multiple: `{"User": ["mixins.AuditMixin", "mixins.TimestampMixin"]}`

When using multiple base classes, the specified classes are used directly without
adding `BaseModel`. Ensure your mixins inherit from `BaseModel` if needed.

**Related:** [`--base-class`](model-customization.md#base-class)

Expand Down
81 changes: 81 additions & 0 deletions docs/jsonschema.md
Original file line number Diff line number Diff line change
Expand Up @@ -124,9 +124,90 @@ class Defaults(BaseModel):

---

## Custom Base Class with `customBasePath`

You can specify custom base classes directly in your JSON Schema using the `customBasePath` extension. This allows you to define base classes at the schema level without using CLI options.

### Single Base Class

```json
{
"$schema": "http://json-schema.org/draft-07/schema#",
"title": "User",
"type": "object",
"customBasePath": "myapp.models.UserBase",
"properties": {
"name": {"type": "string"},
"email": {"type": "string"}
},
"required": ["name", "email"]
}
```

**Generated Output:**

```python
from __future__ import annotations

from myapp.models import UserBase


class User(UserBase):
name: str
email: str
```

### Multiple Base Classes (Mixins)

You can also specify multiple base classes as a list to implement mixin patterns:

```json
{
"$schema": "http://json-schema.org/draft-07/schema#",
"title": "User",
"type": "object",
"customBasePath": ["mixins.AuditMixin", "mixins.TimestampMixin"],
"properties": {
"name": {"type": "string"},
"email": {"type": "string"}
},
"required": ["name", "email"]
}
```

**Generated Output:**

```python
from __future__ import annotations

from mixins import AuditMixin, TimestampMixin


class User(AuditMixin, TimestampMixin):
name: str
email: str
```

!!! note "Mixin Usage"
When using multiple base classes, the specified classes are used directly without adding `BaseModel`.
Ensure your mixins inherit from `pydantic.BaseModel` if you need Pydantic model behavior.

### Priority Resolution

When multiple base class configurations are present, they are resolved in this order:

1. **`--base-class-map`** (CLI option) - Highest priority
2. **`customBasePath`** (JSON Schema extension)
3. **`--base-class`** (CLI option) - Lowest priority (default for all models)

This allows you to set a default base class with `--base-class`, override specific models in the schema with `customBasePath`, and further override at the CLI level with `--base-class-map`.

---

## 📖 See Also

- 🖥️ [CLI Reference](cli-reference/index.md) - Complete CLI options reference
- 🔧 [CLI Reference: Typing Customization](cli-reference/typing-customization.md) - Type annotation options
- 🏷️ [CLI Reference: Field Customization](cli-reference/field-customization.md) - Field naming and constraint options
- 📊 [Supported Data Types](supported-data-types.md) - JSON Schema data type support
- 🏗️ [CLI Reference: Model Customization](cli-reference/model-customization.md) - Base class and model customization options
96 changes: 95 additions & 1 deletion docs/llms-full.txt
Original file line number Diff line number Diff line change
Expand Up @@ -1988,7 +1988,20 @@ The `--base-class` flag configures the code generation behavior.
Specify different base classes for specific models via JSON mapping.

The `--base-class-map` option allows you to assign different base classes
to specific models. Priority: base-class-map > customBasePath > base-class.
to specific models. This is useful when you want selective base class inheritance,
for example, applying custom base classes only to specific models while leaving
others with the default `BaseModel`.

Priority: `--base-class-map` > `customBasePath` (schema extension) > `--base-class`

You can specify either a single base class as a string, or multiple base classes
(mixins) as a list:

- Single: `{"Person": "custom.bases.PersonBase"}`
- Multiple: `{"User": ["mixins.AuditMixin", "mixins.TimestampMixin"]}`

When using multiple base classes, the specified classes are used directly without
adding `BaseModel`. Ensure your mixins inherit from `BaseModel` if needed.

**Related:** [`--base-class`](model-customization.md#base-class)

Expand Down Expand Up @@ -24171,12 +24184,93 @@ class Defaults(BaseModel):

---

## Custom Base Class with `customBasePath`

You can specify custom base classes directly in your JSON Schema using the `customBasePath` extension. This allows you to define base classes at the schema level without using CLI options.

### Single Base Class

```json
{
"$schema": "http://json-schema.org/draft-07/schema#",
"title": "User",
"type": "object",
"customBasePath": "myapp.models.UserBase",
"properties": {
"name": {"type": "string"},
"email": {"type": "string"}
},
"required": ["name", "email"]
}
```

**Generated Output:**

```python
from __future__ import annotations

from myapp.models import UserBase


class User(UserBase):
name: str
email: str
```

### Multiple Base Classes (Mixins)

You can also specify multiple base classes as a list to implement mixin patterns:

```json
{
"$schema": "http://json-schema.org/draft-07/schema#",
"title": "User",
"type": "object",
"customBasePath": ["mixins.AuditMixin", "mixins.TimestampMixin"],
"properties": {
"name": {"type": "string"},
"email": {"type": "string"}
},
"required": ["name", "email"]
}
```

**Generated Output:**

```python
from __future__ import annotations

from mixins import AuditMixin, TimestampMixin


class User(AuditMixin, TimestampMixin):
name: str
email: str
```

!!! note "Mixin Usage"
When using multiple base classes, the specified classes are used directly without adding `BaseModel`.
Ensure your mixins inherit from `pydantic.BaseModel` if you need Pydantic model behavior.

### Priority Resolution

When multiple base class configurations are present, they are resolved in this order:

1. **`--base-class-map`** (CLI option) - Highest priority
2. **`customBasePath`** (JSON Schema extension)
3. **`--base-class`** (CLI option) - Lowest priority (default for all models)

This allows you to set a default base class with `--base-class`, override specific models in the schema with `customBasePath`, and further override at the CLI level with `--base-class-map`.

---

## 📖 See Also

- 🖥️ [CLI Reference](cli-reference/index.md) - Complete CLI options reference
- 🔧 [CLI Reference: Typing Customization](cli-reference/typing-customization.md) - Type annotation options
- 🏷️ [CLI Reference: Field Customization](cli-reference/field-customization.md) - Field naming and constraint options
- 📊 [Supported Data Types](supported-data-types.md) - JSON Schema data type support
- 🏗️ [CLI Reference: Model Customization](cli-reference/model-customization.md) - Base class and model customization options

---

Expand Down
2 changes: 1 addition & 1 deletion src/datamodel_code_generator/__main__.py
Original file line number Diff line number Diff line change
Expand Up @@ -499,7 +499,7 @@ def validate_class_name_affix_scope(cls, v: str | ClassNameAffixScope | None) ->
target_python_version: PythonVersion = PythonVersionMin
target_pydantic_version: Optional[TargetPydanticVersion] = None # noqa: UP045
base_class: str = ""
base_class_map: Optional[dict[str, str]] = None # noqa: UP045
base_class_map: Optional[dict[str, str | list[str]]] = None # noqa: UP045
additional_imports: Optional[list[str]] = None # noqa: UP045
class_decorators: Optional[list[str]] = None # noqa: UP045
custom_template_dir: Optional[Path] = None # noqa: UP045
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ class GenerateConfigDict(TypedDict):
target_python_version: NotRequired[PythonVersion]
target_pydantic_version: NotRequired[TargetPydanticVersion | None]
base_class: NotRequired[str]
base_class_map: NotRequired[dict[str, str] | None]
base_class_map: NotRequired[dict[str, str | list[str]] | None]
additional_imports: NotRequired[list[str] | None]
class_decorators: NotRequired[list[str] | None]
custom_template_dir: NotRequired[Path | None]
Expand Down
2 changes: 1 addition & 1 deletion src/datamodel_code_generator/_types/parser_config_dicts.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ class ParserConfigDict(TypedDict):
data_type_manager_type: NotRequired[type[DataTypeManager]]
data_model_field_type: NotRequired[type[DataModelFieldBase]]
base_class: NotRequired[str | None]
base_class_map: NotRequired[dict[str, str] | None]
base_class_map: NotRequired[dict[str, str | list[str]] | None]
additional_imports: NotRequired[list[str] | None]
class_decorators: NotRequired[list[str] | None]
custom_template_dir: NotRequired[Path | None]
Expand Down
4 changes: 2 additions & 2 deletions src/datamodel_code_generator/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ class Config:
target_python_version: PythonVersion = PythonVersionMin
target_pydantic_version: TargetPydanticVersion | None = None
base_class: str = ""
base_class_map: dict[str, str] | None = None
base_class_map: dict[str, str | list[str]] | None = None
additional_imports: list[str] | None = None
class_decorators: list[str] | None = None
custom_template_dir: Path | None = None
Expand Down Expand Up @@ -225,7 +225,7 @@ class Config:
data_type_manager_type: type[DataTypeManager] = pydantic_model.DataTypeManager
data_model_field_type: type[DataModelFieldBase] = pydantic_model.DataModelField
base_class: str | None = None
base_class_map: dict[str, str] | None = None
base_class_map: dict[str, str | list[str]] | None = None
additional_imports: list[str] | None = None
class_decorators: list[str] | None = None
custom_template_dir: Path | None = None
Expand Down
24 changes: 17 additions & 7 deletions src/datamodel_code_generator/model/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -643,7 +643,7 @@ def __init__( # noqa: PLR0913
fields: list[DataModelFieldBase],
decorators: list[str] | None = None,
base_classes: list[Reference] | None = None,
custom_base_class: str | None = None,
custom_base_class: str | list[str] | None = None,
custom_template_dir: Path | None = None,
extra_template_data: defaultdict[str, dict[str, Any]] | None = None,
methods: list[str] | None = None,
Expand Down Expand Up @@ -781,14 +781,24 @@ def replace_children_in_models(self, models: list[DataModel], new_ref: Reference
child.replace_reference(new_ref)

def set_base_class(self) -> None:
"""Set up the base class for this model."""
base_class = self.custom_base_class or self.BASE_CLASS
if not base_class:
"""Set up the base class(es) for this model."""
if self.custom_base_class is None:
base_class_list = [self.BASE_CLASS] if self.BASE_CLASS else []
elif isinstance(self.custom_base_class, list):
base_class_list = self.custom_base_class
else:
base_class_list = [self.custom_base_class]

if not base_class_list:
self.base_classes = []
return
base_class_import = Import.from_full_path(base_class)
self._additional_imports.append(base_class_import)
self.base_classes = [BaseClassDataType.from_import(base_class_import)]

result = []
for base_class in base_class_list:
base_class_import = Import.from_full_path(base_class)
self._additional_imports.append(base_class_import)
result.append(BaseClassDataType.from_import(base_class_import))
self.base_classes = result

@cached_property
def template_file_path(self) -> Path:
Expand Down
2 changes: 1 addition & 1 deletion src/datamodel_code_generator/model/dataclass.py
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ def __init__( # noqa: PLR0913
fields: list[DataModelFieldBase],
decorators: list[str] | None = None,
base_classes: list[Reference] | None = None,
custom_base_class: str | None = None,
custom_base_class: str | list[str] | None = None,
custom_template_dir: Path | None = None,
extra_template_data: defaultdict[str, dict[str, Any]] | None = None,
methods: list[str] | None = None,
Expand Down
2 changes: 1 addition & 1 deletion src/datamodel_code_generator/model/enum.py
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ def __init__( # noqa: PLR0913
fields: list[DataModelFieldBase],
decorators: list[str] | None = None,
base_classes: list[Reference] | None = None,
custom_base_class: str | None = None,
custom_base_class: str | list[str] | None = None,
custom_template_dir: Path | None = None,
extra_template_data: defaultdict[str, dict[str, Any]] | None = None,
methods: list[str] | None = None,
Expand Down
2 changes: 1 addition & 1 deletion src/datamodel_code_generator/model/msgspec.py
Original file line number Diff line number Diff line change
Expand Up @@ -132,7 +132,7 @@ def __init__( # noqa: PLR0913
fields: list[DataModelFieldBase],
decorators: list[str] | None = None,
base_classes: list[Reference] | None = None,
custom_base_class: str | None = None,
custom_base_class: str | list[str] | None = None,
custom_template_dir: Path | None = None,
extra_template_data: defaultdict[str, dict[str, Any]] | None = None,
methods: list[str] | None = None,
Expand Down
4 changes: 2 additions & 2 deletions src/datamodel_code_generator/model/pydantic/base_model.py
Original file line number Diff line number Diff line change
Expand Up @@ -286,7 +286,7 @@ def __init__( # noqa: PLR0913
fields: list[DataModelFieldBase],
decorators: list[str] | None = None,
base_classes: list[Reference] | None = None,
custom_base_class: str | None = None,
custom_base_class: str | list[str] | None = None,
custom_template_dir: Path | None = None,
extra_template_data: defaultdict[str, Any] | None = None,
path: Path | None = None,
Expand Down Expand Up @@ -343,7 +343,7 @@ def __init__( # noqa: PLR0912, PLR0913
fields: list[DataModelFieldBase],
decorators: list[str] | None = None,
base_classes: list[Reference] | None = None,
custom_base_class: str | None = None,
custom_base_class: str | list[str] | None = None,
custom_template_dir: Path | None = None,
extra_template_data: defaultdict[str, Any] | None = None,
path: Path | None = None,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -245,7 +245,7 @@ def __init__( # noqa: PLR0913
fields: list[DataModelFieldBase],
decorators: list[str] | None = None,
base_classes: list[Reference] | None = None,
custom_base_class: str | None = None,
custom_base_class: str | list[str] | None = None,
custom_template_dir: Path | None = None,
extra_template_data: defaultdict[str, Any] | None = None,
path: Path | None = None,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ def __init__( # noqa: PLR0913
fields: list[DataModelFieldBase],
decorators: list[str] | None = None,
base_classes: list[Reference] | None = None,
custom_base_class: str | None = None,
custom_base_class: str | list[str] | None = None,
custom_template_dir: Path | None = None,
extra_template_data: defaultdict[str, dict[str, Any]] | None = None,
methods: list[str] | None = None,
Expand Down
2 changes: 1 addition & 1 deletion src/datamodel_code_generator/model/scalar.py
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ def __init__( # noqa: PLR0913
fields: list[DataModelFieldBase],
decorators: list[str] | None = None,
base_classes: list[Reference] | None = None,
custom_base_class: str | None = None,
custom_base_class: str | list[str] | None = None,
custom_template_dir: Path | None = None,
extra_template_data: defaultdict[str, dict[str, Any]] | None = None,
methods: list[str] | None = None,
Expand Down
2 changes: 1 addition & 1 deletion src/datamodel_code_generator/model/typed_dict.py
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ def __init__( # noqa: PLR0913
fields: list[DataModelFieldBase],
decorators: list[str] | None = None,
base_classes: list[Reference] | None = None,
custom_base_class: str | None = None,
custom_base_class: str | list[str] | None = None,
custom_template_dir: Path | None = None,
extra_template_data: defaultdict[str, dict[str, Any]] | None = None,
methods: list[str] | None = None,
Expand Down
Loading
Loading