diff --git a/docs/cli-reference/typing-customization.md b/docs/cli-reference/typing-customization.md index 129d34e3e..ad82ff16c 100644 --- a/docs/cli-reference/typing-customization.md +++ b/docs/cli-reference/typing-customization.md @@ -2736,6 +2736,25 @@ The `--type-mappings` flag configures the code generation behavior. Replace schema model types with custom Python types via JSON mapping. +This option is useful for importing models from external libraries (like `geojson-pydantic`) +instead of generating them. + +**Override Formats:** + +| Format | Description | +|--------|-------------| +| `{"ModelName": "package.Type"}` | Model-level: Skip generating `ModelName` and import from `package` | +| `{"Model.field": "package.Type"}` | Scoped: Override only specific field in specific model | + +**Common Use Cases:** + +| Use Case | Example Override | +|----------|------------------| +| GeoJSON types | `{"Feature": "geojson_pydantic.Feature"}` | +| Custom datetime | `{"Timestamp": "pendulum.DateTime"}` | +| MongoDB ObjectId | `{"ObjectId": "bson.ObjectId"}` | +| Custom validators | `{"Email": "my_app.types.ValidatedEmail"}` | + !!! tip "Usage" ```bash @@ -2744,6 +2763,11 @@ Replace schema model types with custom Python types via JSON mapping. 1. :material-arrow-left: `--type-overrides` - the option documented here +!!! note "Model-level overrides skip generation" + When you specify a model-level override (without a dot in the key), the generator will + **skip generating that model entirely** and import it from the specified package instead. + + ??? example "Examples" **Input Schema:** diff --git a/tests/data/expected/main/type_overrides_external_lib.py b/tests/data/expected/main/type_overrides_external_lib.py new file mode 100644 index 000000000..2ff2c0d49 --- /dev/null +++ b/tests/data/expected/main/type_overrides_external_lib.py @@ -0,0 +1,20 @@ +# generated by datamodel-codegen: +# filename: type_overrides_external_lib.json +# timestamp: 1985-10-26T08:21:00+00:00 + +from __future__ import annotations + +from typing import Any + +from geojson_pydantic import Feature, FeatureCollection +from pydantic import BaseModel + + +class Model(BaseModel): + __root__: Any + + +class Place(BaseModel): + name: str | None = None + location: Feature | None = None + boundary: FeatureCollection | None = None diff --git a/tests/data/jsonschema/type_overrides_external_lib.json b/tests/data/jsonschema/type_overrides_external_lib.json new file mode 100644 index 000000000..ca779be90 --- /dev/null +++ b/tests/data/jsonschema/type_overrides_external_lib.json @@ -0,0 +1,30 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "definitions": { + "Feature": { + "type": "object", + "properties": { + "type": {"type": "string"}, + "geometry": {"type": "object"} + } + }, + "FeatureCollection": { + "type": "object", + "properties": { + "type": {"type": "string"}, + "features": { + "type": "array", + "items": {"$ref": "#/definitions/Feature"} + } + } + }, + "Place": { + "type": "object", + "properties": { + "name": {"type": "string"}, + "location": {"$ref": "#/definitions/Feature"}, + "boundary": {"$ref": "#/definitions/FeatureCollection"} + } + } + } +} diff --git a/tests/main/test_main_general.py b/tests/main/test_main_general.py index 086a48c2c..ed5088939 100644 --- a/tests/main/test_main_general.py +++ b/tests/main/test_main_general.py @@ -674,7 +674,31 @@ def test_dataclass_arguments_invalid(json_str: str, match: str) -> None: @pytest.mark.cli_doc( options=["--type-overrides"], - option_description="""Replace schema model types with custom Python types via JSON mapping.""", + option_description="""Replace schema model types with custom Python types via JSON mapping. + +This option is useful for importing models from external libraries (like `geojson-pydantic`) +instead of generating them. + +**Override Formats:** + +| Format | Description | +|--------|-------------| +| `{"ModelName": "package.Type"}` | Model-level: Skip generating `ModelName` and import from `package` | +| `{"Model.field": "package.Type"}` | Scoped: Override only specific field in specific model | + +!!! note "Model-level overrides skip generation" + When you specify a model-level override (without a dot in the key), the generator will + **skip generating that model entirely** and import it from the specified package instead. + +**Common Use Cases:** + +| Use Case | Example Override | +|----------|------------------| +| GeoJSON types | `{"Feature": "geojson_pydantic.Feature"}` | +| Custom datetime | `{"Timestamp": "pendulum.DateTime"}` | +| MongoDB ObjectId | `{"ObjectId": "bson.ObjectId"}` | +| Custom validators | `{"Email": "my_app.types.ValidatedEmail"}` | +""", input_schema="jsonschema/type_overrides_test.json", cli_args=["--type-overrides", '{"CustomType": "my_app.types.CustomType"}'], golden_output="main/type_overrides_model_level.py", @@ -695,6 +719,31 @@ def test_type_overrides_model_level(output_file: Path) -> None: ) +@pytest.mark.cli_doc( + options=["--type-overrides"], + option_description="""Replace schema model types with custom Python types via JSON mapping.""", + input_schema="jsonschema/type_overrides_external_lib.json", + cli_args=[ + "--type-overrides", + '{"Feature": "geojson_pydantic.Feature", "FeatureCollection": "geojson_pydantic.FeatureCollection"}', + ], + golden_output="main/type_overrides_external_lib.py", +) +@freeze_time(TIMESTAMP) +def test_type_overrides_external_lib(output_file: Path) -> None: + """Test --type-overrides with external library types like geojson-pydantic.""" + run_main_and_assert( + input_path=JSON_SCHEMA_DATA_PATH / "type_overrides_external_lib.json", + output_path=output_file, + input_file_type="jsonschema", + assert_func=assert_file_content, + extra_args=[ + "--type-overrides", + '{"Feature": "geojson_pydantic.Feature", "FeatureCollection": "geojson_pydantic.FeatureCollection"}', + ], + ) + + @freeze_time(TIMESTAMP) def test_type_overrides_scoped(output_file: Path) -> None: """Test --type-overrides with scoped override replaces specific field only."""