Skip to content

Commit c944dc0

Browse files
committed
Strip Field None defaults with metadata
1 parent 00cf006 commit c944dc0

7 files changed

Lines changed: 67 additions & 18 deletions

File tree

src/datamodel_code_generator/model/pydantic_base.py

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -235,6 +235,17 @@ def _process_data_in_str(self, data: dict[str, Any]) -> None: # pragma: no cove
235235
def _process_annotated_field_arguments(self, field_arguments: list[str]) -> list[str]: # noqa: PLR6301
236236
return field_arguments
237237

238+
def _should_strip_default_none_from_field(self, default_repr: str) -> bool:
239+
if self.data_type.is_optional:
240+
return False
241+
242+
match default_repr:
243+
case "None":
244+
should_strip = True
245+
case _:
246+
should_strip = False
247+
return should_strip
248+
238249
def _get_field_data_and_default_factory(self) -> tuple[dict[str, Any], Any]:
239250
"""Build Field() keyword data and the effective default_factory."""
240251
data: dict[str, Any] = {k: v for k, v in self.extras.items() if k not in self._EXCLUDE_FIELD_KEYS}
@@ -306,8 +317,11 @@ def __str__(self) -> str:
306317
and not self.extras.get("validate_default")
307318
):
308319
field_arguments = ["...", *field_arguments]
309-
elif not default_factory:
310-
default_repr = represent_python_value(self.default)
320+
elif (
321+
not default_factory
322+
and (default_repr := represent_python_value(self.default)) is not None
323+
and not (self.strip_default_none and self._should_strip_default_none_from_field(default_repr))
324+
):
311325
field_arguments = [default_repr, *field_arguments]
312326

313327
if self.is_class_var:

tests/data/expected/main/jsonschema/root_in_enum.py

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,11 +14,8 @@ class Model(BaseModel):
1414
populate_by_name=True,
1515
)
1616
order_reference: str | None = Field(
17-
None,
1817
alias='orderReference',
1918
description='Reference number of the order',
2019
examples=['27378669'],
2120
)
22-
brand: Literal['OPUS', 'someday'] | None = Field(
23-
None, description='purchased brand'
24-
)
21+
brand: Literal['OPUS', 'someday'] | None = Field(description='purchased brand')

tests/data/expected/main/openapi/pyproject.py

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -55,22 +55,18 @@ class Error(BaseModel):
5555

5656
class Api(BaseModel):
5757
api_key: str | None = Field(
58-
None,
5958
alias="apiKey",
6059
description="To be used as a dataset parameter value",
6160
)
6261
api_version_number: str | None = Field(
63-
None,
6462
alias="apiVersionNumber",
6563
description="To be used as a version parameter value",
6664
)
6765
api_url: AnyUrl | None = Field(
68-
None,
6966
alias="apiUrl",
7067
description="The URL describing the dataset's fields",
7168
)
7269
api_documentation_url: AnyUrl | None = Field(
73-
None,
7470
alias="apiDocumentationUrl",
7571
description="A URL to the API console for each API",
7672
)
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
# generated by datamodel-codegen:
2+
# filename: strip_default_none_pydantic_v2.yaml
3+
# timestamp: 2019-07-26T00:00:00+00:00
4+
5+
from __future__ import annotations
6+
7+
from pydantic import BaseModel, Field
8+
9+
10+
class Issue1816Model(BaseModel):
11+
metadataOnly: str | None = Field(description='Optional field with metadata only.')
12+
explicitDefault: str | None = Field(
13+
'active', description='Optional field with a non-None default.'
14+
)
15+
plainOptional: str | None

tests/data/expected/main/openapi/with_strip_default_none.py

Lines changed: 4 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -41,17 +41,13 @@ class Error(BaseModel):
4141

4242

4343
class Api(BaseModel):
44-
apiKey: str | None = Field(
45-
None, description='To be used as a dataset parameter value'
46-
)
44+
apiKey: str | None = Field(description='To be used as a dataset parameter value')
4745
apiVersionNumber: str | None = Field(
48-
None, description='To be used as a version parameter value'
49-
)
50-
apiUrl: AnyUrl | None = Field(
51-
None, description="The URL describing the dataset's fields"
46+
description='To be used as a version parameter value'
5247
)
48+
apiUrl: AnyUrl | None = Field(description="The URL describing the dataset's fields")
5349
apiDocumentationUrl: AnyUrl | None = Field(
54-
None, description='A URL to the API console for each API'
50+
description='A URL to the API console for each API'
5551
)
5652

5753

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
openapi: 3.0.3
2+
info:
3+
title: strip default none regression
4+
version: 1.0.0
5+
paths: {}
6+
components:
7+
schemas:
8+
Issue1816Model:
9+
type: object
10+
properties:
11+
metadataOnly:
12+
type: string
13+
description: Optional field with metadata only.
14+
explicitDefault:
15+
type: string
16+
description: Optional field with a non-None default.
17+
default: active
18+
plainOptional:
19+
type: string

tests/main/openapi/test_main_openapi.py

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1307,6 +1307,18 @@ def test_main_with_strip_default_none(output_file: Path) -> None:
13071307
)
13081308

13091309

1310+
def test_main_openapi_pydantic_v2_strip_default_none_field_metadata(output_file: Path) -> None:
1311+
"""Test strip-default-none removes implicit None defaults from Field metadata."""
1312+
run_main_and_assert(
1313+
input_path=OPEN_API_DATA_PATH / "strip_default_none_pydantic_v2.yaml",
1314+
output_path=output_file,
1315+
input_file_type="openapi",
1316+
assert_func=assert_file_content,
1317+
expected_file="strip_default_none_pydantic_v2.py",
1318+
extra_args=["--strip-default-none", "--output-model-type", "pydantic_v2.BaseModel"],
1319+
)
1320+
1321+
13101322
def test_disable_timestamp(output_file: Path) -> None:
13111323
"""Test OpenAPI generation with timestamp disabled."""
13121324
run_main_and_assert(

0 commit comments

Comments
 (0)