Skip to content

Fix RootModel default value not being applied#2960

Merged
koxudaxi merged 2 commits intomainfrom
fix/rootmodel-default-value
Jan 12, 2026
Merged

Fix RootModel default value not being applied#2960
koxudaxi merged 2 commits intomainfrom
fix/rootmodel-default-value

Conversation

@koxudaxi
Copy link
Copy Markdown
Owner

@koxudaxi koxudaxi commented Jan 12, 2026

Summary by CodeRabbit

  • Documentation

    • Updated model and template customization guides with default values for root fields in example code.
  • Tests

    • Updated test expectations to reflect default value assignments for root model fields across multiple test scenarios.

✏️ Tip: You can customize this high-level summary in your review settings.

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Jan 12, 2026

📝 Walkthrough

Walkthrough

Default values are added to __root__ and root fields across Pydantic model definitions in documentation examples and test expected output files. Changes systematically assign defaults (e.g., 1 for integers, 'abc' for strings, constrained values for floats) to root model fields throughout the repository.

Changes

Cohort / File(s) Summary
Documentation Examples
docs/cli-reference/model-customization.md, docs/cli-reference/template-customization.md
Added default values to __root__ field declarations: __root__: int__root__: int = 1 and confloat(ge=0.0, le=25.0)confloat(ge=0.0, le=25.0) = 0
Test Expected Output - JSON Schema
tests/data/expected/main/jsonschema/all_of_any_of_base_class_ref.py, tests/data/expected/main/jsonschema/has_default_value.py
Added default values to root fields in Zoom class (= 0) and multiple classes in has_default_value (ID, Family, FamilyPets with values 'abc', ['abc', 'efg'], ['taro', 'shiro'] respectively)
Test Expected Output - OpenAPI Nullable
tests/data/expected/main/openapi/nullable.py, tests/data/expected/main/openapi/nullable_strict_nullable.py, tests/data/expected/main/openapi/nullable_strict_nullable_use_union_operator.py, tests/data/expected/main/openapi/use_default_kwarg.py
Added default value = 1 to __root__: int fields in Id class across multiple nullable test scenarios
Test Expected Output - OpenAPI Referenced Defaults
tests/data/expected/main/openapi/referenced_default.py, tests/data/expected/main/openapi/referenced_default_use_annotated.py
Added default value = 5 to constrained float root fields (ModelSettingB) in both standard and Annotated variants

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~8 minutes

Suggested labels

safe-to-fix

Suggested reviewers

  • ilovelinux

Poem

🐰 Root fields now have defaults in hand,
Through docs and tests, a simple brand,
No breaking changes, just a gentle touch—
The models are stronger, they do so much!
Default values bloom like carrots so fine. 🥕

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and specifically describes the main change: fixing RootModel default value application across multiple test files and documentation.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
  • 📝 Generate docstrings

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Generated by GitHub Actions
@github-actions
Copy link
Copy Markdown
Contributor

📚 Docs Preview: https://pr-2960.datamodel-code-generator.pages.dev

@codspeed-hq
Copy link
Copy Markdown

codspeed-hq Bot commented Jan 12, 2026

CodSpeed Performance Report

Merging this PR will degrade performance by 17.78%

Comparing fix/rootmodel-default-value (0d9ff79) with main (aa088d6)

⚠️ Unknown Walltime execution environment detected

Using the Walltime instrument on standard Hosted Runners will lead to inconsistent data.

For the most accurate results, we recommend using CodSpeed Macro Runners: bare-metal machines fine-tuned for performance measurement consistency.

Summary

❌ 11 regressed benchmarks
⏩ 98 skipped benchmarks1

⚠️ Please fix the performance issues or acknowledge them on CodSpeed.

Performance Changes

Mode Benchmark BASE HEAD Efficiency
WallTime test_perf_deep_nested 5.1 s 6.2 s -17.78%
WallTime test_perf_openapi_large 2.5 s 2.9 s -15.91%
WallTime test_perf_duplicate_names 849.3 ms 1,021.7 ms -16.88%
WallTime test_perf_graphql_style_pydantic_v2 703.2 ms 850.9 ms -17.36%
WallTime test_perf_complex_refs 1.7 s 2.1 s -17.45%
WallTime test_perf_all_options_enabled 5.7 s 6.9 s -16.96%
WallTime test_perf_large_models_pydantic_v2 3.1 s 3.8 s -17.63%
WallTime test_perf_kubernetes_style_pydantic_v2 2.2 s 2.7 s -17.06%
WallTime test_perf_aws_style_openapi_pydantic_v2 1.7 s 2 s -16.5%
WallTime test_perf_stripe_style_pydantic_v2 1.7 s 2 s -16.75%
WallTime test_perf_multiple_files_input 3.2 s 3.8 s -17.18%

Footnotes

  1. 98 benchmarks were skipped, so the baseline results were used instead. If they were deleted from the codebase, click here and archive them to remove them from the performance reports.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Fix all issues with AI agents
In @tests/data/expected/main/jsonschema/has_default_value.py:
- Around line 27-28: The class Family defines __root__: list[ID] but the default
is raw strings ['abc','efg'], which leaves __root__ as strings at runtime;
replace the default with actual ID instances (or use a default_factory that
constructs a list of ID(...) objects) so __root__'s default matches the
annotated type ID and will be valid BaseModel instances when Family is
instantiated.
📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between aa088d6 and 0d9ff79.

⛔ Files ignored due to path filters (3)
  • docs/llms-full.txt is excluded by none and included by none
  • src/datamodel_code_generator/model/template/pydantic/BaseModel_root.jinja2 is excluded by none and included by none
  • src/datamodel_code_generator/model/template/pydantic_v2/RootModel.jinja2 is excluded by none and included by none
📒 Files selected for processing (10)
  • docs/cli-reference/model-customization.md
  • docs/cli-reference/template-customization.md
  • tests/data/expected/main/jsonschema/all_of_any_of_base_class_ref.py
  • tests/data/expected/main/jsonschema/has_default_value.py
  • tests/data/expected/main/openapi/nullable.py
  • tests/data/expected/main/openapi/nullable_strict_nullable.py
  • tests/data/expected/main/openapi/nullable_strict_nullable_use_union_operator.py
  • tests/data/expected/main/openapi/referenced_default.py
  • tests/data/expected/main/openapi/referenced_default_use_annotated.py
  • tests/data/expected/main/openapi/use_default_kwarg.py
🧰 Additional context used
🧠 Learnings (2)
📓 Common learnings
Learnt from: koxudaxi
Repo: koxudaxi/datamodel-code-generator PR: 2890
File: tests/data/expected/main/jsonschema/ref_nullable_with_constraint.py:14-15
Timestamp: 2026-01-02T08:25:22.111Z
Learning: The datamodel-code-generator currently generates RootModel subclasses with an explicit `root` field annotation (e.g., `class StringType(RootModel[str]): root: str`). This is existing behavior of the code generator and should not be flagged as an issue introduced by new changes.
📚 Learning: 2026-01-02T08:25:22.111Z
Learnt from: koxudaxi
Repo: koxudaxi/datamodel-code-generator PR: 2890
File: tests/data/expected/main/jsonschema/ref_nullable_with_constraint.py:14-15
Timestamp: 2026-01-02T08:25:22.111Z
Learning: The datamodel-code-generator currently generates RootModel subclasses with an explicit `root` field annotation (e.g., `class StringType(RootModel[str]): root: str`). This is existing behavior of the code generator and should not be flagged as an issue introduced by new changes.

Applied to files:

  • tests/data/expected/main/openapi/nullable_strict_nullable_use_union_operator.py
  • tests/data/expected/main/jsonschema/all_of_any_of_base_class_ref.py
  • tests/data/expected/main/openapi/referenced_default.py
  • tests/data/expected/main/jsonschema/has_default_value.py
  • tests/data/expected/main/openapi/referenced_default_use_annotated.py
  • docs/cli-reference/model-customization.md
  • docs/cli-reference/template-customization.md
  • tests/data/expected/main/openapi/nullable_strict_nullable.py
  • tests/data/expected/main/openapi/nullable.py
  • tests/data/expected/main/openapi/use_default_kwarg.py
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (4)
  • GitHub Check: 3.10 on Windows
  • GitHub Check: 3.11 on Windows
  • GitHub Check: 3.12 on Windows
  • GitHub Check: 3.13 on Windows
🔇 Additional comments (11)
tests/data/expected/main/openapi/nullable_strict_nullable_use_union_operator.py (1)

59-60: LGTM!

The default value addition for Id.__root__ is consistent with the PR objective and aligns with other root models in this file that already have defaults (Description, Name).

tests/data/expected/main/openapi/use_default_kwarg.py (1)

59-60: LGTM!

The default value for Id.__root__ correctly reflects the expected code generator output when default values are specified in the schema.

docs/cli-reference/template-customization.md (1)

2628-2628: LGTM!

The documentation example now correctly shows the default value = 0 for Zoom.__root__, which matches the "default": 0 specified in the input JSON schema (line 2502). This accurately demonstrates the fixed behavior.

tests/data/expected/main/openapi/referenced_default_use_annotated.py (1)

12-13: LGTM!

The default value = 5 for ModelSettingB.root is consistent with the usage pattern in the Model class where settingA defaults to 5 and settingB uses ModelSettingB(5) as its default factory.

tests/data/expected/main/openapi/nullable.py (1)

59-60: LGTM!

The default value addition completes the fix for Id.__root__, consistent with other root models in this file that already correctly have their defaults applied.

tests/data/expected/main/openapi/referenced_default.py (1)

10-11: LGTM!

The default value 5 is correctly within the constrained float range [0.0, 10.0], and is consistent with the default_factory on line 16.

tests/data/expected/main/openapi/nullable_strict_nullable.py (1)

59-60: LGTM!

The default value 1 correctly matches the int type declaration.

tests/data/expected/main/jsonschema/all_of_any_of_base_class_ref.py (1)

77-78: LGTM!

The default value 0 is valid for the constrained float range [0.0, 25.0] and is consistent with the default_factory on line 21.

docs/cli-reference/model-customization.md (1)

5031-5032: LGTM!

The documentation example correctly reflects the new generator behavior for applying default values to root model fields.

tests/data/expected/main/jsonschema/has_default_value.py (2)

19-20: LGTM!

The default value 'abc' correctly matches the str type for the ID root model.


31-32: Type mismatch in generated code: list[Pet] default contains strings instead of Pet objects.

This is the expected output generated from the JSON schema's FamilyPets definition, which specifies a default of ["taro", "shiro"] (strings) for an array of Pet items. The generated code faithfully reproduces this mismatch: __root__: list[Pet] = ['taro', 'shiro'].

Unlike the Family field which uses list[ID] with string defaults (valid because ID is a root model wrapping str), Pet is a regular model with properties. Pydantic will attempt to coerce the string defaults to Pet instances during model instantiation, which will likely fail since strings cannot be automatically converted to objects.

The schema definition itself contains this problematic default value combination.

Comment thread tests/data/expected/main/jsonschema/has_default_value.py
@codecov
Copy link
Copy Markdown

codecov Bot commented Jan 12, 2026

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 100.00%. Comparing base (aa088d6) to head (0d9ff79).
⚠️ Report is 1 commits behind head on main.

Additional details and impacted files
@@            Coverage Diff            @@
##              main     #2960   +/-   ##
=========================================
  Coverage   100.00%   100.00%           
=========================================
  Files           94        94           
  Lines        17738     17738           
  Branches      2038      2038           
=========================================
  Hits         17738     17738           
Flag Coverage Δ
unittests 100.00% <ø> (ø)

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@koxudaxi koxudaxi merged commit 4cbf3bf into main Jan 12, 2026
37 of 38 checks passed
@koxudaxi koxudaxi deleted the fix/rootmodel-default-value branch January 12, 2026 14:39
@github-actions
Copy link
Copy Markdown
Contributor

Breaking Change Analysis

Result: Breaking changes detected

Reasoning: This PR is a bug fix that corrects RootModel default value handling. While it fixes incorrect behavior, it changes the generated code output for any schema that defines default values for root models. Users who rely on the current generated code (without defaults) or have custom templates will need to account for this change. The template logic change from field.required to (field.required and not field.has_default) also requires users with custom templates to update them.

Content for Release Notes

Code Generation Changes

  • RootModel default values now included in generated code - Previously, default values defined in JSON Schema or OpenAPI specifications for root models were not being applied to the generated Pydantic code. Now these defaults are correctly included. For example, a schema defining a root model with default: 1 will generate __root__: int = 1 (Pydantic v1) or root: int = 1 (Pydantic v2) instead of just __root__: int or root: int. This may affect code that relied on the previous behavior where RootModel fields had no default values. (Fix RootModel default value not being applied #2960)

Custom Template Update Required

  • Template condition for default values changed - If you use custom Jinja2 templates based on BaseModel_root.jinja2 or RootModel.jinja2, the condition for including default values has changed from field.required to (field.required and not field.has_default). Update your custom templates if you override these files. (Fix RootModel default value not being applied #2960)

This analysis was performed by Claude Code Action

@github-actions
Copy link
Copy Markdown
Contributor

🎉 Released in 0.53.0

This PR is now available in the latest release. See the release notes for details.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant