NewYour coding agent can read the release notes before it upgrades.Set up the MCP server →
PyPI · #976 most downloaded on PyPI
Datamodel Code Generator
Last release 10 days ago
24 Sep 2026
Ships on a steady schedule
a new release about every 9 days
Nearly every release is documented
notes for 60 of the last 60 stable releases
1 version withdrawn
withdrawn after publishing
7 years old
287 releases · first in 2019
Deduplication now compares referenced models - Models that render identically are only merged when the models they reference also match structurally,
LeafModel or Values1, changing class names and module contents compared with previous output (#4115)$dynamicRef are now specialized per dynamic scope - A plain-name $dynamicRef previously resolved lexically within its own schema resource, so every referrer shared one model of the referenced generic. The parser now tracks the dynamic scope of the schemas that referenced a model, binds each $dynamicAnchor name to its outermost declaration in that scope, and re-parses a referenced schema as an additional specialized model whenever its bindings differ. Schemas that combine $dynamicRef with $dynamicAnchor, including remote, embedded, recursive, root-level and tuple additionalItems generics, now emit extra model classes with suffixed names and different field types than before, so generated module contents, class names and import sites can change for existing users (#4117)Model suffixes. Output ordering and names of surrounding models can therefore differ from previous releases, and this applies under --naming-strategy full-path and --collapse-root-models as well (#4117)parse_raw now drains pending dynamic-scope specializations after every specification has been read, so components that reference external schemas rebinding a $dynamicAnchor produce additional specialized models instead of reusing a single shared model (#4117)$dynamicRef is now resolved within the schema resource a subschema stands for, namely an inline subschema declaring its own $id and the target of a $ref merged with sibling keywords, with enclosing and referencing resources still able to override the binding so the outermost $dynamicAnchor declaration wins; schemas combining $dynamicRef with embedded $id resources or with $ref plus sibling keywords may now generate different field types and model shapes than before, for example a resource-local anchor type such as RootModel[str] where the previous output used the anchor visible in the outer scope (#4119)--reuse-model, --collapse-reuse-models and --reuse-scope=tree previously matched models on rendered output and imports alone, so models that rendered identically but referenced different models sharing a name were incorrectly collapsed into one. Duplicate detection within a module and across modules now also requires the referenced models to match, so these models stay distinct. Affected schemas now generate additional or differently named classes, such as a suffixed FieldModel alongside Field, extra per-module classes, and inheritance from a shared base instead of a single merged model, which can change class names and module layout that downstream code imports (#4121)$ref targets now resolve through aliases - A referenced schema that is itself a $ref is merged with its own target, so a $ref with sibling keywords pointing at an alias now generates the aliased definition instead of the alias node, changing model names and field types in generated output for JSON Schema 2020-12 documents that use $ref alongside other keywords (#4122)$ref with sibling keywords points into another document, nested $ref and plain-name $dynamicRef values are rewritten to absolute locations before merging, so schemas whose relative references previously resolved against the wrong base now produce different models, imports, and field types (#4122)$recursiveRef in merged cross-document $ref targets - A self-pointing $recursiveRef inside a schema merged in from another document now resolves to the nearest enclosing $recursiveAnchor of its defining document, or that document's root, instead of resolving against the merging document; schemas that rely on the previous resolution generate different recursive model references and field annotations, so regenerated models may name different classes than before, and when the merging document itself declares $recursiveAnchor the reference is instead left to extend the dynamic scope (#4127)_prepare_schema_resources rather than the original raw specification, so specifications that use $id inside component schemas can produce different models, model names, and field types than before (#4128)Tree, Node, or Branch now reuse the single shared model, so generated class names, class counts, and field annotations can change and code importing the previously emitted duplicate class names will need updating (#4133)$id resources resolve to the resource root - A $recursiveRef located in a subschema that declares its own $id but no $recursiveAnchor now targets the root of that embedded resource instead of the nearest enclosing $recursiveAnchor or the document root, both when the document is parsed directly and when its schemas are merged into another document, so affected fields are annotated with the embedded resource model instead of the outer model, and targets reachable only through $recursiveRef are now loaded and can add generated models (#4130)Field or FieldModel; it is now named after the file stem, for example Tree or Node, so class names and field annotations such as list[Field] change for such schemas (#4132)allOf base class is referenced from a schema whose dynamic scope would rebind its $dynamicAnchor, the parser now emits a UserWarning stating that the base class resolves its $dynamicRef within its own schema resources. Generation still succeeds, but builds that treat warnings as errors or assert on captured warnings can now fail (#4117)SchemaResourceRefWarning, so generation runs that previously completed silently can now surface warnings and can fail where warnings are configured as errors (#4128)--external-ref-mapping entries must now name the path of the document the reference actually comes from rather than a path relative to the merged target, so existing mappings that relied on the previous matching may silently stop applying (#4122)Full Changelog: 0.82.0...0.83.0
One column per quarter.
Constraints are now emitted on recursive container fields - Previously any field that was a self reference had all of its constraints suppressed. The
_can_apply_constraints property in the base field model only suppresses constraints for direct self references, so a self referencing field whose type is a container such as list, Sequence, dict, Mapping, set, frozenset, or tuple now keeps its size constraints. Pydantic output gains Field(min_length=..., max_length=...) and msgspec output gains Meta(min_length=..., max_length=...) where the generated code previously had a bare default, for example friends: list[Pet] | None = None becomes friends: list[Pet] | None = Field(None, min_length=1) (#4071)minItems or maxItems on a recursive array is rejected at validation time instead of being accepted. Existing code that relied on the previously unconstrained generated models may start raising pydantic ValidationError or msgspec.ValidationError for payloads that used to pass (#4071)Full Changelog: 0.81.0...0.82.0
Undeclared required keys now retained in variant runtime validation - When combining --read-only-write-only-model-type request/response variants with
--read-only-write-only-model-type request/response variants with --schema-validator-type runtime validators, generated models now keep runtime validation rules for required keys that are not declared as properties (for example keys required via additionalProperties or if/then/else), whereas these rules were previously dropped from the variant models. Regenerated Request and Response models therefore enforce stricter validation and may reject payloads that the previously generated validators accepted (#4055)Full Changelog: 0.80.0...0.81.0
Python union annotations preserve unsupported member types - When generating from Python input models, unions containing members such as Callable reta
Callable retain each member type and the original union order instead of repeating the first non-null type. Regenerated annotations change for the affected unions and nested containers. (#3947)--use-default-factory-for-optional-nested-models, an optional child with required constructor fields, including inherited fields, keeps its normal None or UNSET default instead of receiving a factory that fails when called without arguments. (#3953)--use-root-model-type-alias, constraints are retained in the alias where supported; affected roots otherwise use a RootModel class instead of an unconstrained alias. Unconstrained aliases retain their prior form, and an existing custom root-alias template keeps its selection and remains responsible for its own constraints. (#3962)propertyNames generate inline constrained string key types instead of nested-model keys. This changes ordinary key annotations for affected schemas; custom schema runtime validators remain conditional on --schema-validator-type pydantic-v2. (#3970)--schema-validator-type pydantic-v2, required property names absent from generated fields are checked against the raw object input, including applicable pattern intersections. Payloads missing those required names are rejected where the previous validators accepted them; this fix does not add a validator when the option is disabled. (#3988)allOf roots with an outer format or type mapping, compatible numeric or string constraints are preserved while constraints incompatible with the mapped runtime type are omitted. Regenerated annotations change for these roots and avoid applying incompatible constraints to date, UUID, or other mapped values. (#4043)propertyNames alternatives combining ordinary string constraints with non-string-only branches, generated dictionary keys use the applicable constrained string type instead of retaining the non-string alternatives. References, complex patterns, and unsupported cases retain the existing fallback. (#4046)--type-mappings or --type-overrides remap Avro logical types to physical types such as int or bytes, affected defaults use the physical Avro value instead of a logical constructor such as a datetime, decimal, or duration expression. (#4031)Full Changelog: 0.79.0...0.80.0
Reject unsupported msgspec enum members - When generating msgspec.Struct output, enums that would render as a plain Enum and contain bool or float mem
msgspec.Struct output, enums that would render as a plain Enum and contain bool or float members now raise an error such as msgspec.Struct does not support bool Enum members, instead of producing code; schemas that previously generated output will now fail and no file is written (#3928)msgspec.Struct models with more than one base class, the generator now validates the inherited slot layouts and raises an Error refusing to write any output when the generated bases would produce incompatible instance layouts, whereas previously it emitted a module that only failed later at Python import time; both the CLI (which now exits with an error and writes no file) and the Python API (which now raises Error) are affected for such schemas (#3929)aliases option is used, an alias value that collides with another field, is not a valid Python identifier, is a Python keyword, or conflicts with a reserved Pydantic or msgspec attribute name now raises an Error and stops generation instead of being silently sanitized or deduplicated (#3936)aliases option are now preserved as the chosen field names and checked against each output backend's naming rules, so configurations that previously produced auto-adjusted output for dataclasses, msgspec, or TypedDict backends may now fail instead of quietly renaming the field (#3936)dict, so non-dict mapping objects such as UserDict or MappingProxyType bypassed pattern-property, required-group, conditional-required, property-count, and unique-items checks; they are now treated like dictionaries and validated, which can raise a ValidationError for mapping inputs that previously passed through silently (#3975)$id are now resolved within the containing document before any file or HTTP lookup, and resource-scoped anchors and JSON pointers are honored, so schemas that previously resolved such references to physical files or remote URLs (including cases where an embedded resource shares a physical filename) can now produce different generated models and different fetch behavior (#3977)treat-dot-as-module is combined with all-exports-scope, export depth and collision prefixes are recomputed against the final package layout and the empty-package re-export __init__.py files are emitted after module post-processing, so projects generated with both options together will see different import paths and __init__.py contents than before (#3926)_1, _2 suffixes when they collide with another validator, an existing field name, or an inherited validator, so models using the validators feature with inheritance or overlapping field names can produce method names that differ from previously generated output (#3942)Optional as Optional_aliased, Field as Field_aliased, or list as list_aliased_2 and rewrites the affected annotations and defaults to reference the alias; this runs by default with no opt-in flag, so generated output changes for any schema whose field names collide with imported names (#3943)_json_schema_conditional_equal helper and stop relying on Python in membership, so when an if then else conditional required rule has const or enum values that include booleans or the integers 0 and 1 (or nested objects and arrays containing them) the generated code now distinguishes booleans from numbers and compares objects and arrays deeply, changing both the generated output and the runtime acceptance of payloads for schemas built with schema-validator-type pydantic-v2 (#3960)from collections.abc import Mapping as _Mapping import and every isinstance(data, dict) guard becomes isinstance(data, dict) or isinstance(data, _Mapping), while structural match cases change from case dict() to case dict() or _Mapping(); users who compare regenerated output against committed golden files will see these differences (#3975)Full Changelog: 0.78.0...0.79.0
MCP tools schema-instance values preserved verbatim - Converting MCP tools inputs now keeps values under default , const , enum , and examples unchang
default, const, enum, and examples unchanged instead of rewriting internal definition references contained within them, so generated model defaults for MCP tools schemas that embed ref-like values inside these instance keywords can differ from previously generated output (#3891)~0 and ~1 tilde escapes are unescaped, following RFC evaluation order, so a $ref fragment that combines percent-encoding with tilde escapes such as a%7E1b now resolves to a different definition than before and can change the generated models for those schemas (#3893)_intersect_constraint logic no longer coerces minimum, maximum, exclusiveMinimum, and exclusiveMaximum operands to float before comparing them, so integer bounds beyond the 2^53 exact-float precision limit are now intersected exactly instead of after lossy float rounding, changing the generated ge, le, gt, and lt values for schemas that merge such large integer bounds via allOf (#3900)multipleOf, exclusiveMinimum, and exclusiveMaximum now retain exact integer values instead of coercing them to floats, so regenerated models for schemas containing integers beyond float precision emit different constraint literals and validate differently than before (#3903)allof_merge_mode is any value other than none, overlapping numeric, length, item, and property-count bounds from allOf subschemas are intersected to the tightest value and enum values are reduced to their intersection, instead of the previous deep-merge or concatenation behavior, so generated constraints and enum members differ from prior releases (#3961)field_constraints and generate_schema_validators both enabled and builtin types in use, a root-level array whose items declare a single ASCII-alphanumeric patternProperties key mapping to a length-bounded string now generates a constr adapter carrying min_length/max_length instead of a plain str, so regenerated models change output and enforce those bounds, rejecting values that previously passed validation (#3992)maxOccurs, an unbounded maximum is now propagated instead of computing a finite upper bound, so an unbounded parent combined with a finite child (and similar nesting) no longer emits a max_length constraint, changing the generated output for affected schemas (#3939)SchemaParseError and aborts instead of producing a widened or concatenated enum, so schemas that previously generated successfully can now fail (#3961)black or isort formatters through the CLI, resolved configuration, or the Python API now emits a FutureWarning announcing that Black/isort will become optional in a future release; previously explicit formatter selection produced no warning. Black and isort remain required dependencies and generated output is unchanged, but strict setups that treat FutureWarning as an error may now fail unless the warning is filtered or disable-warnings is used (#4009)Full Changelog: 0.77.0...0.78.0
Nested Avro bytes and fixed defaults are now decoded to bytes - Avro bytes and fixed defaults nested inside arrays, maps, and records are now converte
bytes and fixed defaults nested inside arrays, maps, and records are now converted to Python bytes literals such as b'\xff', whereas previously only top-level bytes and fixed defaults were converted and nested ones remained as strings, so regenerating affected Avro schemas produces different default values across the pydantic v2, dataclass, and msgspec backends (#3896)Field(default_factory=...) and add a from pydantic import Field import instead of the previous plain default rendering, so regenerating existing schemas produces different output; ClassVar and plain required dictionary fields are additionally kept free of Field statements (#3883)as alias are split onto separate lines, so files regenerated from the same input will differ from output produced by earlier versions (#3874)bytes and fixed fields carrying a decimal logical type with a default now decode the default as a signed big-endian coefficient scaled by the declared scale and emit a Decimal(...) expression for backends that support deserialized defaults, replacing the previous raw bytes literal output, so regenerating existing models yields different default values (#3902)date, time-millis, time-micros, timestamp-millis, timestamp-micros, timestamp-nanos, or the local-timestamp variants, an integer default is now rendered as a temporal constructor like datetime_module.date.fromisoformat or an ISO-8601 string for backends that map these formats to str, instead of the previous raw integer literal, so generated models change for any schema using these defaults; timestamp defaults use UTC while local-timestamp and time defaults are naive (#3901)fixed fields using the duration logical type with a default now emit timedelta(milliseconds=N) values instead of the raw bytes previously produced, changing generated output for any schema that relies on such defaults (#3907)1 lexical value - Elements declared with nillable="1" are now treated the same as nillable="true" and generate a nullable field, so schemas that previously produced a required field for nillable="1" will now produce an optional (nullable) field (#3884)xs:pattern facets previously kept only the last pattern value, and now the parser merges all supported sibling patterns into a single anchored alternation and intersects any inherited base pattern through a nested lookahead, so regenerating from such schemas produces different pattern strings than before (#3908)regex_engine="python-re" configuration entry that was not present before, because the generated anchored lookahead requires Pydantic's Python regex engine, changing the generated model configuration for affected fields (#3908)convert to construct the referenced Struct instance, whereas previously such defaults were emitted as plain literal values because only direct Struct references were converted; empty collections, mapping and dict defaults, primitive aliases, and recursive non-model aliases keep their prior output (#3906)false now raise an Error reading "Referenced MCP boolean false definition is not supported" instead of emitting a permissive model, so schemas that previously generated code may now fail during conversion (#3890)root - When emitting model metadata for RootModel classes, the emitted field entry now uses root for both its name and alias instead of the previously emitted synthetic field name, so consumers of the emitted metadata for root models will see different name and alias values (#3897)generate_dynamic_models runtime helper now includes specialized RootModel type-alias classes that are assigned at the top level of the generated code, whereas it previously omitted any class whose defining module was not the generated module, so existing callers receive additional entries in the returned mapping while genuine imported dependencies remain excluded (#3904)Full Changelog: 0.76.2...0.77.0
These corrections change generated output or existing configuration behavior for the affected cases. Regenerate and review affected models before upgr
These corrections change generated output or existing configuration behavior for the affected cases. Regenerate and review affected models before upgrading snapshots or downstream integrations.
bytes and fixed defaults become Python bytes using Avro's code-point mapping, including named fixed types. Review constructor requirements and default values. This release does not yet recursively decode nested bytes defaults. (#3842)Query or Mutation are no longer excluded solely by name. References to omitted operation roots become Any instead of unresolved class names. Review imports of previously emitted root classes and affected annotations. (#3842)None alongside UnsetType, and boolean enum values use supported non-literal types rather than boolean Literal annotations. TypedDict extra-item types now bring their required imports. Review generated annotations, default factories, and snapshots. (#3840)other.json#name resolves a matching anchor within that schema resource before falling back to the legacy /name pointer. Schemas that relied on the old pointer interpretation when an anchor also exists can resolve to a different definition. (#3838)pyproject.toml resolve from the configuration file's directory. This applies to input, output, custom headers/templates, comparison output, model metadata, local HTTP references, and lockfiles, including batch jobs. When invoking from another directory, adjust relative values or use absolute paths if you previously relied on the working directory. Explicit CLI paths retain their command-line interpretation. (#3845)use-annotated = false is respected. Implicit field constraints follow the final annotation setting. Review output if CLI, presets, and pyproject.toml previously supplied conflicting options. (#3845)Error diagnostics rather than leaking the previous lower-level exceptions. CLI configuration validation and input-decoding failures report concise errors. Update exception handlers or exact-error assertions where applicable. (#3838, #3842, #3845):80 or :443 from an omitted port. (#3844)Full Changelog: 0.76.1...0.76.2
Parser source loading, run state, output-model capabilities, field-name policies, and input-model transport have been separated to clarify internal ow
x-enum-descriptions now accepts null entries, allowing schemas with missing individual enum descriptions to generate successfully. (#3835)Full Changelog: 0.76.0...0.76.1
--list-deprecations output changed. Table and Markdown output add Status and rename the Warning since heading to Since . Table lines no longer retain…
propertyNames. When additionalProperties specifies a value schema, its generated dictionary key type now includes supported string-key constraints from propertyNames, instead of always using plain str. Regenerated annotations and imports change, and keys that violate those constraints can fail validation. Review affected extra-property inputs and generated-code snapshots. (#3794)--list-deprecations output changed. Table and Markdown output add Status and rename the Warning since heading to Since. Table lines no longer retain trailing padding. JSON adds status while retaining the existing warning_since key. Update scripts that depend on exact columns, whitespace, or JSON field sets. (#3810)# Before (table)
ID Kind Target Warning since Removal Replacement
# After (table)
ID Status Kind Target Since Removal Replacement
DefaultValueTypeWarning; the existing generated default stays unchanged when Decimal deserialization is disabled. Use --deserialize-default-values decimal to generate compatible Decimal defaults, or account for this warning if your application promotes warnings to errors. The new dated standard-*-20260826 and practical-*-20260826 presets enable Decimal and enum default deserialization; existing *-20260619 presets remain available. (#3792, #3811)--set-default-enum-member is registered as a scheduled deprecation. It still works and does not emit a runtime deprecation warning in this release. Prefer --deserialize-default-values enum; no removal version is set. (#3810, #3811)Full Changelog: 0.75.1...0.76.0
Update CHANGELOG for 0.75.0 by @dcg-generated-docs[bot] in #3787
Full Changelog: 0.75.0...0.75.1
minProperties/maxProperties now generate runtime validators - When using the experimental --generate-schema-validators option, minProperties / maxProp
--generate-schema-validators option, minProperties/maxProperties constraints on named object models are now emitted as Pydantic v2 model validators. Previously these constraints were ignored. Data that omits or exceeds the allowed property count will now be rejected at validation time, and generated models gain a new __json_schema_property_count_rule__ class variable (#3780)_JsonSchemaRuntimeValidationBase is now split: the core helper is renamed to _JsonSchemaRuntimeValidationBaseCore and a new _JsonSchemaRuntimeValidationBase subclass is inserted. Code that references the generated helper class name by hand will need to be updated (#3780)# Before (--generate-schema-validators)
class _JsonSchemaRuntimeValidationBase(BaseModel):
__json_schema_conditional_required__: ClassVar[tuple[Any, ...]] = ()
...
class ApiConditionalEnvelopeRequestModel(_JsonSchemaRuntimeValidationBase):
__json_schema_conditional_required__: ClassVar[tuple[Any, ...]] = (...)
# After
class _JsonSchemaRuntimeValidationBaseCore(BaseModel):
__json_schema_conditional_required__: ClassVar[tuple[Any, ...]] = ()
...
class _JsonSchemaRuntimeValidationBase(_JsonSchemaRuntimeValidationBaseCore):
__json_schema_property_count_rule__: ClassVar[tuple[Any, ...]] = ()
...
class ApiConditionalEnvelopeRequestModel(_JsonSchemaRuntimeValidationBase):
__json_schema_property_count_rule__: ClassVar[tuple[Any, ...]] = (2, 2)
__json_schema_conditional_required__: ClassVar[tuple[Any, ...]] = (...)Full Changelog: 0.74.0...0.75.0
Non-finite float values now render as structural float(...) expressions - Generated code for non-finite floats ( inf , -inf , nan ) from Protocol Buff
float(...) expressions - Generated code for non-finite floats (inf, -inf, nan) from Protocol Buffers, XML Schema, and AsyncAPI-embedded schemas — as field defaults, constraint bounds, list items, and enum values — now renders inline as float('inf'), float('-inf'), and float('nan') instead of emitting bare inf/nan literals with an injected from math import inf, nan header. Users relying on the previous output (e.g. snapshot/golden tests, or importing inf/nan from the generated module) will see different generated code (#3770)# Before
from math import inf
class Model(BaseModel):
value: float | None = inf
# After
class Model(BaseModel):
value: float | None = float('inf')Full Changelog: 0.73.0...0.74.0
Additional imports are now validated as Python import paths - Values passed via --additional-imports , the Python config API ( GenerateConfig , JSONSc
--additional-imports, the Python config API (GenerateConfig, JSONSchemaParserConfig, etc.), or --extra-template-data must now be dotted sequences of Python identifiers. Previously any value was accepted and split on commas without validation; now inputs that are not valid import paths (e.g. containing newlines, semicolons, or non-identifier syntax) raise an Error and abort generation instead of being emitted into the generated output. Valid dotted paths (optionally whitespace-padded) continue to work unchanged. (#3763)additional_imports must be a Python import path composed of identifiers: 'collections.deque\nINJECTION_MARKER = 1'
--extra-template-data now raise an error for built-in templates - When rendering a built-in (project-owned) template, supplying any generator-reserved key through --extra-template-data (or the extra_template_data API argument) now raises an Error and aborts generation instead of injecting the value. The reserved keys are class_body_lines, config_items, schema_runtime_validation, schema_runtime_validation_base_class_name, schema_runtime_validation_use_base, sequence_base_class, sequence_item_type, sequence_slice_type, _safe_config_items, typed_dict_kwargs, and typed_dict_kwargs_suffix. To inject raw code via these keys you must now use a custom root template through --custom-template-dir. (#3765)extra_template_data validation - extra_template_data that is not a dictionary, contains non-string keys, or contains duplicate (normalized) keys now raises an Error rather than being silently accepted. (#3765)extra_template_data values as non-executing literals - For built-in templates, user-supplied values that were previously emitted as raw Python source are now serialized as quoted, non-executing literals. This affects GraphQL scalar py_type, TypedDict additionalPropertiesType, ConfigDict values, msgspec base_class_kwargs, and comments. Only bare or dotted identifiers (e.g. datetime.date) are still emitted unquoted; more complex expressions become string literals. For example, a scalar py_type supplied as a type expression is now rendered as:Evil = TypeAliasType("Evil", "__import__('os').system('id') or str")Trusted custom root templates (--custom-template-dir providing the root template) keep the previous unrestricted raw behavior. (#3765)
template_file_path.is_absolute() to _uses_custom_root_template. A --custom-template-dir that only supplies include/partial templates (not the model's root template) no longer opts the built-in root into the unrestricted raw-context path; its extra_template_data is now treated with the hardened built-in rules (and reserved keys raise an error). (#3765)Full Changelog: 0.72.4...0.73.0
Update CHANGELOG for 0.72.3 by @dcg-generated-docs[bot] in #3724
Full Changelog: 0.72.3...0.72.4
Update CHANGELOG for 0.72.2 by @dcg-generated-docs[bot] in #3715
Full Changelog: 0.72.2...0.72.3
Bump aiohttp from 3.13.3 to 3.14.3 by @dependabot [bot] in #3701
Full Changelog: 0.72.1...0.72.2
Add TypedDict total=False option by @koxudaxi in #3689
Full Changelog: 0.72.0...0.72.1
Deprecated decorator detection now requires an exact name match - The check that decides whether a @deprecated class decorator is already present chan…
{}) default whose type is a nested Struct reference now renders a conversion factory instead of a plain dict factory, changing the runtime default from an empty dict to an actual model instance. No CLI flag is required to trigger this. Regenerating existing schemas that have empty-object defaults for nested Struct fields will produce different output and different default values (#3668)# Before
nested: Nested | UnsetType = field(default_factory=dict)
# After
nested: Nested | UnsetType = field(
default_factory=lambda: convert({}, type=Nested)
)--use-default-factory-for-optional-nested-models, optional nested Pydantic dataclass fields now emit Field(default_factory=<Nested>) where they previously did not, because the field now resolves Pydantic dataclass references. Output for these fields changes on regeneration when that flag is enabled (#3668)# After (with --use-default-factory-for-optional-nested-models)
nested: Nested | None = Field(default_factory=Nested)dict), so regenerating existing schemas can produce different output. The constructor form is only used when every required argument of the nested model is satisfied and the mapping is not recursive; otherwise the previous dict-literal default is preserved. (#3669)# Before
@dataclass
class Model:
inner: Inner | None = field(default_factory=lambda: {'v': float('inf')})
# After
@dataclass
class Model:
inner: Inner | None = field(default_factory=lambda: Inner(v=float('inf')))@deprecated class decorator is already present changed from a text prefix match (decorator.startswith("@deprecated")) to an exact unqualified-name match (is_named_python_decorator(decorator, "deprecated")). If a schema marks a class as deprecated and you also apply a custom class decorator that merely shares the deprecated prefix (e.g. @deprecated_custom), the generator previously treated it as the deprecated decorator and skipped emitting one; it now recognizes them as distinct and adds the real @deprecated('... is deprecated.') decorator along with the typing_extensions.deprecated import. Generated output for these cases changes accordingly. Idempotency for the standard @deprecated('...') decorator is unchanged. (#3685)# Input: deprecated class + custom decorator "@deprecated_custom"
# Before
@deprecated_custom
@dataclass
class LegacyUser:
...
# After
@deprecated_custom
@deprecated('LegacyUser is deprecated.')
@dataclass
class LegacyUser:
...Full Changelog: 0.71.0...0.72.0
New warning emitted by default for unresolved local $ref pointers - By default, an unresolved local $ref JSON pointer now emits a new DanglingRefWarni
$ref pointers - By default, an unresolved local $ref JSON pointer now emits a new DanglingRefWarning and generates a fallback Any model. Previously such refs resolved to a silent fallback, and some cases (e.g., out-of-range JSON pointer array indices) raised a hard error. Schemas that previously generated cleanly can now produce warnings, which breaks workflows that treat warnings as errors (e.g., python -W error). Use --strict-refs to fail on unresolved pointers instead, or --disable-warnings to silence them (#3639)$ref to a non-dict file now raises InvalidFileFormatError instead of TypeError - Resolving a $ref to a JSON/YAML file whose top-level value is not a mapping (e.g., a list) now raises datamodel_code_generator.InvalidFileFormatError (a subclass of Error/Exception) rather than the built-in TypeError. Programmatic callers that catch TypeError around generate() must catch InvalidFileFormatError (or Error) instead (#3639)Invalid file format for <type> at <source>: ..., and the missing-file message changed from File not found to File not found: <path>. Tooling that matches on the exact previous strings needs updating (#3639)generate() Python API and the CLI. Invocations that previously succeeded now raise an Error (CLI exits with an error code) in these cases: the output path resolves to an existing input file, the --emit-model-metadata path resolves to an existing input file, or the output and model-metadata paths resolve to the same file. Symlinks and hardlinks are resolved before the comparison. Workflows that intentionally wrote output over an input path, or that pointed the model-metadata artifact at the same path as the output, will now fail with one of:Output path must not overwrite an input path: <path>
Model metadata path must not overwrite an input path: <path>
Output and model metadata paths must be different: <path>
(#3647)
Full Changelog: 0.70.0...0.71.0
Preserved additionalProperties value constraints change generated output - JSON Schema additionalProperties (and propertyNames/mapping) value schemas
additionalProperties (and propertyNames/mapping) value schemas that carry constraints (e.g. minimum/maximum, minLength/maxLength/pattern, array item constraints, allOf-merged primitives) now retain those constraints in the generated model instead of dropping them. This produces new TypeAlias/RootModel/TypeAliasType definitions and changes dict value type hints, so output differs for existing schemas. For example, a mapping value that previously generated dict[str, Literal['fixed']] now generates a dedicated alias preserving the constraints (#3616):# Before
class DictModel(RootModel[dict[str, Literal['fixed']]]):
root: dict[str, Literal['fixed']]
# After
DictModelAdditionalProperty = TypeAliasType(
"DictModelAdditionalProperty",
Annotated[Literal['fixed'], Field(max_length=100, min_length=1)],
)
class DictModel(RootModel[dict[str, DictModelAdditionalProperty]]):
root: dict[str, DictModelAdditionalProperty]
Full Changelog: https://github.com/koxudaxi/datamodel-code-generator/compare/0.69.0...0.70.0
Discriminated unions with duplicate or unresolvable discriminator values now fall back to a plain union - When a discriminated union contains variants
RootModel/type-alias wrappers, None/str members, or otherwise lack a resolvable discriminator literal, the generator no longer emits Field(..., discriminator='...') and instead produces a regular union. Previously such schemas emitted a discriminator= argument. Users regenerating models from these schemas will see the discriminator keyword removed from the affected fields (#3603)# Before (invalid duplicate-value discriminator was emitted):
class GroupedItem(RootModel[Item | ItemReference]):
root: Item | ItemReference = Field(..., discriminator='type')
# After (falls back to a plain union when variants are not valid discriminated members):
class MixedItem(RootModel[str | ItemReference | None]):
root: str | ItemReference | None
Full Changelog: https://github.com/koxudaxi/datamodel-code-generator/compare/0.68.1...0.69.0
Skip docs preview deploy for dependabot PRs by @gaborbernat in https://github.com/koxudaxi/datamodel-code-generator/pull/3576
Full Changelog: https://github.com/koxudaxi/datamodel-code-generator/compare/0.68.0...0.68.1
Ruff formatting failures now raise instead of being ignored - Ruff subprocess calls previously ran with check=False and used their stdout regardless o
check=False and used their stdout regardless of exit code, so a failed ruff run silently produced (possibly empty or partial) output. _run_ruff_command now raises RuntimeError when ruff exits non-zero (except the allowed check-with-remaining-diagnostics case), and _find_ruff_path raises a RuntimeError with an install hint when ruff is missing instead of falling back to "ruff". Workflows that relied on generation silently succeeding despite a ruff error will now fail with an exception. (#3561)--enable-command-header now redacts sensitive HTTP options - When --enable-command-header is combined with --http-headers or --http-query-parameters, the reproducibility command line embedded in generated files now writes <redacted> in place of those values instead of the literal arguments. Generated file headers change for these flag combinations, so committed output or snapshot tests will differ. (#3561)RootModel now synchronizes and renders model_config derived from config objects (e.g. regex_engine, frozen) that were previously dropped, and non-dict config objects are now applied in the base model. Generated output for affected pydantic v2 root models may now include a model_config = ConfigDict(...) that was not emitted before. (#3561)generate() keeps str inputs as inline source text. Pass a Path to input_ for local file input; if a failed string input resolves to an existing path, generate() warns and recommends using Path. (#3573)Full Changelog: https://github.com/koxudaxi/datamodel-code-generator/compare/0.67.0...0.68.0
Auto input-type CSV fallback is now conditional - When --input-file-type auto is used, infer_input_type() previously treated any YAML parse failure as
--input-file-type auto is used, infer_input_type() previously treated any YAML parse failure as CSV data. It now falls back to CSV only when the text looks like CSV (non-empty lines with consistent, non-zero comma counts). Inputs that were previously inferred as csv — including malformed JSON/YAML and ragged CSV with inconsistent column counts (e.g. id,name,tel\n1,taro\n) — will no longer be inferred as CSV. (#3536)extra-args no longer undergoes shell expansion - The Action previously appended extra-args directly into the shell command (datamodel-codegen "${ARGS[@]}" ${{ inputs.extra-args }}), so the value was subject to word-splitting, glob expansion, variable expansion, and command substitution. It is now tokenized with Python's shlex.split. Workflows that relied on shell features inside extra-args (globs like *.json, variables like $HOME, or command substitution $(...)) will no longer have them expanded and must pass literal, individually-quoted arguments instead. (#3542)extras input is now validated against a whitelist - Previously any value passed to the extras input was accepted and injected into the pip install spec. It is now validated against the fixed set graphql, http, validation, ruff, all; any other value now fails the install step with ::error::Unsupported extras value: <value> instead of being passed through. Workflows passing extras values outside this set will now error. (#3542)auto inference, input that fails YAML parsing and does not look like CSV now raises Error rather than falling back to CSV and attempting generation. The error message was also expanded to include the underlying YAML parser error, e.g. Can't infer input file type from the input data. YAML parser error: <Type>: <detail>. Please specify the input file type explicitly with --input-file-type option. Workflows depending on the old blanket-CSV fallback must now pass --input-file-type csv explicitly. (#3536)Full Changelog: https://github.com/koxudaxi/datamodel-code-generator/compare/0.66.3...0.67.0
Update CHANGELOG for 0.66.2 by @dcg-generated-docs[bot] in https://github.com/koxudaxi/datamodel-code-generator/pull/3511
Full Changelog: https://github.com/koxudaxi/datamodel-code-generator/compare/0.66.2...0.66.3
Update CHANGELOG for 0.66.1 by @dcg-generated-docs[bot] in https://github.com/koxudaxi/datamodel-code-generator/pull/3507
Full Changelog: https://github.com/koxudaxi/datamodel-code-generator/compare/0.66.1...0.66.2
Add schema validators by @koxudaxi in https://github.com/koxudaxi/datamodel-code-generator/pull/3228
Full Changelog: https://github.com/koxudaxi/datamodel-code-generator/compare/0.66.0...0.66.1
msgspec nullable annotated fields now place None outside Annotated - For msgspec.Struct models, nullable fields that carry Meta/Annotated constraints
None outside Annotated - For msgspec.Struct models, nullable fields that carry Meta/Annotated constraints no longer wrap the type in Optional inside the Annotated[...]. The None member is now emitted as a separate union arm outside the Annotated wrapper, which changes the generated type hints and the resulting imports (Optional is dropped when no longer needed). This is required for the generated models to validate correctly at runtime under msgspec, but it changes output for existing users. (#3495)# Before
from typing import Annotated, Optional, Union
name: Union[Annotated[Optional[str], Meta(max_length=5)], UnsetType] = UNSET
# After
from typing import Annotated, Union
name: Union[Annotated[str, Meta(max_length=5)], None, UnsetType] = UNSET
Full Changelog: https://github.com/koxudaxi/datamodel-code-generator/compare/0.65.1...0.66.0
Update CHANGELOG for 0.65.0 by @dcg-generated-docs[bot] in https://github.com/koxudaxi/datamodel-code-generator/pull/3470
Full Changelog: https://github.com/koxudaxi/datamodel-code-generator/compare/0.65.0...0.65.1
Fix release draft breaking change notes by @koxudaxi in https://github.com/koxudaxi/datamodel-code-generator/pull/3440
!!set tags now rejected - load_yaml now validates all YAML input and raises a yaml.YAMLError when it encounters the unsupported tag:yaml.org,2002:set (!!set) tag. Previously such input was loaded silently (constructed as a Python set). YAML schemas or sample data that relied on !!set tags will now fail to load with an "Unsupported YAML tag" error. This validation runs unconditionally on every YAML load and is not gated behind any opt-in flag (#3447)bool instead of Literal[True]/Literal[False] - When generating msgspec.Struct models (--output-model-type msgspec.Struct), a JSON Schema const with a boolean value now produces a plain bool type instead of a boolean Literal, because msgspec does not support boolean Literal tag values at runtime. Output for other model types (e.g. Pydantic) is unchanged. Existing msgspec users with boolean-const fields will see different generated output. (#3462)msgspec.Struct output, combined (oneOf/anyOf) object schemas that share a required literal discriminator field with int/str values now have a discriminator inferred and are emitted as tagged Struct unions (tag_field=..., tag=...). This changes the generated class definitions for existing msgspec users whose schemas match this shape. (#3462)Full Changelog: https://github.com/koxudaxi/datamodel-code-generator/compare/0.64.1...0.65.0
Optional primitive const fields no longer emit the const value as an injected default - Optional primitive const properties without a schema default n
const fields no longer emit the const value as an injected default - Optional primitive const properties without a schema default now render as nullable/omittable (Literal[...] | None = None) instead of being populated with the const value when the input key is omitted. Regenerated code and snapshot tests may change. (#3434)Full Changelog: https://github.com/koxudaxi/datamodel-code-generator/compare/0.64.0...0.64.1
Pin deprecation warning stacklevel by @koxudaxi in https://github.com/koxudaxi/datamodel-code-generator/pull/3363
--disable-future-imports - When --disable-future-imports is set (no from __future__ import annotations and no native PEP 649 deferred evaluation on Python < 3.14), self-referencing and forward-referencing field annotations in regular BaseModel classes are now emitted as quoted forward references instead of bare names. Previously such annotations were left unquoted, producing invalid code that raised NameError (Ruff F821) at class-evaluation time. Output for the common case (with from __future__ import annotations or Python 3.14 native deferred annotations) is unchanged. Users who snapshot/golden-file generated output for the --disable-future-imports configuration with self-referencing models will see the annotation change from unquoted to quoted, e.g. children: Optional[List[Node]] → children: Optional[List["Node"]]. (#3387)constr() for string fields carrying minItems/maxItems by @DarkaMaul in https://github.com/koxudaxi/datamodel-code-generator/pull/3353Full Changelog: https://github.com/koxudaxi/datamodel-code-generator/compare/0.63.0...0.64.0
--check output now uses POSIX-style paths - File paths in --check diagnostic output are now normalized with Path.as_posix() instead of str(path). On P
--check output now uses POSIX-style paths - File paths in --check diagnostic output are now normalized with Path.as_posix() instead of str(path). On POSIX systems the output is unchanged, but on Windows the diff headers and MISSING:/EXTRA: lines now use forward slashes (models/foo.py) instead of backslashes (models\foo.py). Tooling or snapshot tests that parse --check output on Windows may need updating. (#3287)Authorization, Cookie, and Proxy-Authorization when a remote schema fetch follows a cross-origin redirect. (GHSA-r5vv-ff45-prp2)Full Changelog: https://github.com/koxudaxi/datamodel-code-generator/compare/0.62.0...0.63.0
Local JSON Schema $ref references outside the input base path now emit deprecation warnings - Relative path refs (e.g., $ref: "../other/schema.json")…
str | None) in msgspec Structs were incorrectly rendered with = None. They are now correctly rendered without a default assignment (e.g., label: str | None instead of label: str | None = None), which means callers must provide the value explicitly (#3292)minItems/maxItems schema constraints on array fields were previously silently ignored for msgspec output. They now render as Annotated[list[T], Meta(min_length=..., max_length=...)] when --use-annotated is enabled (e.g., list[Pet] becomes Annotated[list[Pet], Meta(max_length=10, min_length=1)]) (#3292)kw_only=True - When a child Struct inherits optional fields from a parent and adds required fields, the child now renders with kw_only=True as a base class kwarg (e.g., class Child(Base, kw_only=True):), changing the calling convention to keyword-only arguments (#3292)float() calls instead of math imports - Generated code previously rendered inf, -inf, and nan (with from math import inf, nan). Now renders float('inf'), float('-inf'), and float('nan') without the math import. Semantically equivalent but changes generated output. (#3289)Decimal() objects - condecimal() constraints previously used bare int/float values (e.g., condecimal(ge=0, le=1000)). Now uses Decimal objects (e.g., condecimal(ge=Decimal('0'), le=Decimal('1000'))) with an added from decimal import Decimal import in generated code. (#3289)gt: 0.5 becomes ge: 1, lt: 2.5 becomes le: 2, and multiple_of: 1 is dropped entirely. Previously these fractional values were passed through as-is to conint() or Field(). (#3289)min_items and max_items schema constraints on array fields are now emitted as Meta(min_length=..., max_length=...) annotations in msgspec output. Previously these constraints were silently ignored. (#3289)= None - Required nullable fields in msgspec Structs previously received a = None default assignment. Now they are rendered without a default, requiring callers to provide the value explicitly. (#3289)extra_items now uses direct identifiers instead of string forward references - When additionalProperties references another type, the generated extra_items= keyword argument changed from a quoted string to a bare identifier (e.g., extra_items='BaseExtra' → extra_items=BaseExtra). The generated code remains valid because files include from __future__ import annotations, but the textual output differs (#3301)TypedDict('Name', {...})) are now rendered before the definition instead of after it. Previously the docstring appeared as a string literal following the assignment; it now appears as a comment block preceding it (#3301)uuid.UUID instead of pydantic.UUID2 - Schemas using "format": "uuid2" now generate UUID imported from the uuid standard library instead of the non-existent UUID2 from pydantic. This is a bug fix since pydantic.UUID2 was never a valid Pydantic v2 type (the old generated code would raise ImportError), but the generated import and type annotation will differ for any schema using this format (#3297)not field.field and (not field.required or field.use_default_with_required or field.data_type.is_optional or field.nullable) to not field.field and (not field.required or field.use_default_with_required). Users with custom msgspec templates that replicate the old logic should update accordingly (#3292)not field.required or field.use_default_with_required or field.data_type.is_optional or field.nullable to not field.required or field.use_default_with_required. Custom templates derived from the built-in msgspec template may need to be updated. (#3289)TypedDictFunction.jinja2 template restructured - The built-in functional TypedDict template moved the description block from after the class definition to before it and added support for field-level docstrings. Users with custom templates that were based on or extend this template may need to update accordingly (#3301)xs:include, xs:import, xs:redefine, or xs:override with a schemaLocation that resolves outside the input base directory now raises an unconditional error. Previously, these references were silently resolved regardless of location. Users with XML schemas that include files from parent or sibling directories must move the included schemas under the input directory before generating models. (#3308)$ref references outside the input base path now emit deprecation warnings - Relative path refs (e.g., $ref: "../other/schema.json") and file:// URL refs that resolve outside the input base directory now emit a FutureWarning under the default behavior (no --allow-remote-refs flag). Previously these were silently resolved. Pass --allow-remote-refs to suppress the warning, or move referenced schemas under the input directory. (#3308)--no-allow-remote-refs now blocks file:// URLs and local refs outside the base path - Previously --no-allow-remote-refs only blocked HTTP(S) $ref fetching. It now also blocks file:// URL references and relative local $ref references that resolve outside the input base path. Users who rely on file:// refs or out-of-tree local refs with --no-allow-remote-refs must switch to --allow-remote-refs or restructure their schemas. (#3308)Full Changelog: https://github.com/koxudaxi/datamodel-code-generator/compare/0.61.0...0.62.0
HTTP(S) schema fetching now blocks localhost, loopback, private, link-local, reserved, and other non-public network targets by default. Users who inte
--allow-private-network or set allow_private_network=True.--url and remote JSON Schema/OpenAPI $ref URLs. (GHSA-rfr2-mq9m-x2qx, GHSA-954p-556p-r752)$ref fetching remains controlled by --allow-remote-refs; non-public remote references additionally require --allow-private-network.Full Changelog: https://github.com/koxudaxi/datamodel-code-generator/compare/0.60.2...0.61.0
Fixed code injection from schema-provided default_factory values. (GHSA-386q-5hp3-95m9)
default_factory values. (GHSA-386q-5hp3-95m9)x-python-type extension values. (GHSA-m34r-v34r-rf9q)comment entries supplied through --extra-template-data. (GHSA-wjv6-jcfj-mf9r)--validators or --extra-template-data. (GHSA-8m8r-38jm-f355)Full Changelog: https://github.com/koxudaxi/datamodel-code-generator/compare/0.60.1...0.60.2
Fixed code injection from GraphQL union descriptions containing carriage returns. (GHSA-j884-q54q-mmx3)
Full Changelog: https://github.com/koxudaxi/datamodel-code-generator/compare/0.60.0...0.60.1
Avro record field defaults are no longer emitted as generated Python defaults. Avro defaults describe reader behavior, not Python model construction d
default. This also applies to Avro schemas embedded in AsyncAPI multi-format schemas. (#3256)xs:decimal now generates Decimal instead of float; xs:dateTime defaults to standard-library datetime because XML Schema allows values without a timezone; xs:dateTimeStamp remains AwareDatetime; xs:duration and xs:yearMonthDuration now generate str, while xs:dayTimeDuration still generates timedelta. (#3248)= [] can now be omitted when constructing the generated model. (#3255)bytes defaults are now generated as bytes literals instead of strings, including escaped byte sequences. (#3252)--output-datetime-class is respected for XML Schema xs:dateTime and xs:dateTimeStamp when explicitly provided. Without the option, the XML Schema defaults above are used. (#3266)--set-default-enum-member. The default behavior is not changed to force enum member references. (#3264)const values are not treated as generated Python defaults unless the schema also defines a default. XML Schema fixed keeps its XSD value-constraint behavior. (#3268)Full Changelog: https://github.com/koxudaxi/datamodel-code-generator/compare/0.59.1...0.60.0
Update CHANGELOG for 0.59.0 by @dcg-generated-docs[bot] in https://github.com/koxudaxi/datamodel-code-generator/pull/3216
Full Changelog: https://github.com/koxudaxi/datamodel-code-generator/compare/0.59.0...0.59.1
Add deprecation registry and docs by @koxudaxi in https://github.com/koxudaxi/datamodel-code-generator/pull/3188
propertyNames constraints - When a JSON Schema uses both patternProperties and propertyNames, the generated dict key type now merges constraints from both. Previously, propertyNames constraints such as minLength, maxLength, and $ref could be ignored when patternProperties was present. Regenerated code may produce stricter key types and reject data that was previously accepted by less-strict generated models. (#3192)additionalProperties now generates a typed __pydantic_extra__ field for Pydantic v2 models - When a JSON Schema defines schema-valued additionalProperties, generated Pydantic v2 models now include __pydantic_extra__: dict[str, <type>]. Previously, these models only allowed extra fields without typing their values. This changes generated output and makes Pydantic validate extra field values at runtime. (#3205)--use-annotated may change - Dataclass field assignment detection now accounts for constraints moved into Annotated[...]. Generated dataclass fields may be reordered so fields without defaults come before fields with defaults. (#3203)--input-file-type auto) now recognizes AsyncAPI, Avro, and Protocol Buffers inputs - Inputs that previously fell back to JSON/YAML handling or failed detection may now be detected as one of these formats. (#3194, #3195, #3198)pydantic>=2,<3 to pydantic>=2.6,<3 was reverted before this release. The final dependency range for Python < 3.14 remains pydantic>=2,<3. (#3210, #3215)Full Changelog: https://github.com/koxudaxi/datamodel-code-generator/compare/0.58.0...0.59.0
Added --serialization-aliases for Pydantic v2 serialization alias mapping.
--serialization-aliases for Pydantic v2 serialization alias mapping. (#3146)--openapi-include-info-version to emit OPENAPI_INFO_VERSION from OpenAPI info.version. (#3176)--use-object-type to generate object instead of Any for unspecified JSON Schema object and array values. (#3177)AliasChoices - Fields that previously generated duplicate entries such as AliasChoices('endDate', 'end_date', 'endDate') now generate each alias once. Runtime behavior is equivalent, but exact generated output changes. (#3146)additionalProperties/unevaluatedProperties, const: null, complex enum values, all-false patternProperties, non-string propertyNames, boolean array item schemas, contains, minProperties/maxProperties, and enum references through allOf now generate more accurate annotations or constraints. Users with snapshots or exact-output checks may see diffs. (#3167)TypedDict from both typing and typing_extensions; TypedDict is kept only where required. (#3155)$ref types in allOf, additionalProperties with $ref, heterogeneous root constraints, and unresolved discriminator fields now generate more valid types/fields. (#3168)allOf schemas now generate root-style payload types - Primitive-only allOf and top-level allOf combined with oneOf/anyOf now generate RootModel/root payload types instead of empty or object-like models. Code instantiating the previous generated classes may need updates. (#3169, #3171)multipleOf intersections in allOf now use the least common multiple - For example, multipleOf: 5 combined with multipleOf: 10 now generates multiple_of=10 instead of incorrectly keeping the first value. Decimal multiples are handled similarly. (#3172)contentEncoding, contentMediaType, contentSchema, externalDocs, and xml are included in generated json_schema_extra when present in the input schema, even without --model-extra-keys. (#3175)--output-datetime-class now rejects incompatible TypedDict and Dataclass output combinations - Pydantic-specific datetime classes with typing.TypedDict, and incompatible dataclass API usage, now raise errors instead of silently producing fallback output. (#3155, #3169)false inside allOf now raises SchemaParseError - Unsatisfiable allOf branches are reported instead of generating incorrect models. false branches in oneOf/anyOf are filtered where appropriate. (#3168)Full Changelog: https://github.com/koxudaxi/datamodel-code-generator/compare/0.57.0...0.58.0
--use-default no longer makes required fields nullable - Previously, --use-default turned required fields into optional nullable fields (e.g., status:
--use-default no longer makes required fields nullable - Previously, --use-default turned required fields into optional nullable fields (e.g., status: str | None = 'active'). Now required fields keep their original non-nullable type and just get the default value rendered (e.g., status: str = 'active'). Users whose downstream code depends on these fields being Optional/nullable will need to update. (#3054)--use-default - Previously, required fields referencing models (e.g., shipping_address: Address) inconsistently rendered defaults with validate_default=True while scalar required fields did not. Now all required fields consistently omit defaults unless --use-default is passed. Users who relied on the previous behavior where model-ref required fields had defaults rendered will see those defaults removed. (#3054)field.use_default_with_required - The built-in templates for BaseModel, dataclass, pydantic_v2/dataclass, and msgspec were updated to check field.use_default_with_required alongside field.required when deciding whether to render defaults. Custom templates that replicate the old default-rendering logic (e.g., {%- if not field.required %}) will still work but won't support the new --use-default behavior for required fields. To get the updated behavior, custom templates should change conditions like not field.required to (not field.required or field.use_default_with_required). (#3054)Full Changelog: https://github.com/koxudaxi/datamodel-code-generator/compare/0.56.1...0.57.0
Fix --base-class-map and --enum-field-as-literal-map long inline json support by @ilovelinux in https://github.com/koxudaxi/datamodel-code-generator/p
--base-class-map and --enum-field-as-literal-map long inline json support by @ilovelinux in https://github.com/koxudaxi/datamodel-code-generator/pull/3075Full Changelog: https://github.com/koxudaxi/datamodel-code-generator/compare/0.56.0...0.56.1
Update release draft model and preserve breaking changes by @koxudaxi in https://github.com/koxudaxi/datamodel-code-generator/pull/3057
Field(default_value, validate_default=True) instead of default_factory=lambda: TypeAdapter(...).validate_python(...) or default_factory=lambda: Model.model_validate(...). This produces simpler, more readable code but changes the generated output format. (#3050)TypeAdapter from pydantic since validate_default=True handles validation natively. (#3050)Field(<raw_value>, validate_default=True) instead of default_factory=lambda: Model.model_validate(...), default_factory=lambda: TypeAdapter(...).validate_python(...), or default_factory=lambda: Model(...). Empty collection defaults changed from default_factory=list/default_factory=dict to Field([], validate_default=True)/Field({}, validate_default=True). The generated code is semantically equivalent under Pydantic v2 but textually different, which will break snapshot tests or tooling that matches exact output. pydantic.TypeAdapter is no longer imported in generated code. (#3070)validate_default=True instead of default_factory lambdas - Fields with structured defaults (dicts, lists, or scalars referencing Pydantic models/RootModels) previously generated default_factory=lambda: ModelName.model_validate(value) or default_factory=lambda: ModelName(value). They now generate Field(value, validate_default=True), producing simpler but different output. Empty collection defaults changed from default_factory=list/default_factory=dict to Field([], validate_default=True)/Field({}, validate_default=True). Users who regenerate code will see different output. (#3071)
Before:count: CountType | None = Field(default_factory=lambda: CountType(10))
items: dict[str, Item] | None = Field(default_factory=dict, title='Items')
After:count: CountType | None = Field(10, validate_default=True)
items: dict[str, Item] | None = Field({}, title='Items', validate_default=True)
validate_default=True instead of default_factory=lambda: - Fields with structured defaults (dicts/lists) that reference Pydantic models previously generated default_factory=lambda: Model.model_validate(...) or default_factory=lambda: TypeAdapter(Type).validate_python(...) patterns. They now generate the raw default value directly with validate_default=True (e.g., Field({'key': 'val'}, validate_default=True) instead of Field(default_factory=lambda: Model.model_validate({'key': 'val'}))). This changes the generated code output and may affect users who depend on the exact generated code structure, pin generated output in tests, or use custom post-processing. The runtime behavior should be equivalent for Pydantic v2 users. (#3072)TypeAdapter import removed from generated code - Generated code no longer imports pydantic.TypeAdapter for default value handling. Code that previously used TypeAdapter(...).validate_python(...) in default factories now uses inline defaults with validate_default=True. (#3072)int and bool discriminator values (e.g., Literal[1] instead of Literal['1']), which changes generated code for schemas using integer discriminator mappings. (#3072)ValidatedDefault and WrappedDefault classes removed - These internal classes were exported from datamodel_code_generator.model.base and have been removed. Code importing these types will break:# Before (broken)
from datamodel_code_generator.model.base import ValidatedDefault, WrappedDefault
(#3050)SUPPORTS_WRAPPED_DEFAULT and SUPPORTS_VALIDATED_DEFAULT class variables removed - These flags were removed from the DataModel base class. Custom model classes that override these variables will see attribute errors. (#3050)ValidatedDefault and WrappedDefault removed - The datamodel_code_generator.model._types module was deleted and ValidatedDefault/WrappedDefault are no longer exported from datamodel_code_generator.model.base. Code that imports or subclasses these types will break. The SUPPORTS_WRAPPED_DEFAULT and SUPPORTS_VALIDATED_DEFAULT class variables were removed from DataModel and its subclasses; custom model classes referencing these attributes will need updating. (#3070)WrappedDefault, ValidatedDefault classes and SUPPORTS_WRAPPED_DEFAULT, SUPPORTS_VALIDATED_DEFAULT class variables - The WrappedDefault and ValidatedDefault classes from datamodel_code_generator.model._types (re-exported via datamodel_code_generator.model.base) have been deleted. The DataModel class variables SUPPORTS_WRAPPED_DEFAULT and SUPPORTS_VALIDATED_DEFAULT have also been removed. Code that imports or references these will break. (#3071)--allow-remote-refs / --no-allow-remote-refs CLI option and allow_remote_refs config field - Remote $ref fetching over HTTP/HTTPS now emits a deprecation warning by default. Pass --allow-remote-refs to suppress the warning, or --no-allow-remote-refs to block remote fetching entirely. In a future version, remote fetching will be disabled by default. Users relying on remote $ref resolution should add --allow-remote-refs to their invocations to avoid the deprecation warning and prepare for the future default change. (#3072)SchemaFetchError exception for HTTP fetch failures - Remote schema fetching now raises SchemaFetchError (instead of propagating raw httpx exceptions) on HTTP errors, non-2xx status codes, or unexpected HTML responses. Users catching specific httpx exceptions from remote ref resolution will need to catch SchemaFetchError instead. (#3072)$ref now raises Error instead of FileNotFoundError - Previously, when a $ref pointed to a non-existent local file, a raw FileNotFoundError propagated to callers. Now it raises datamodel_code_generator.Error with the message "$ref file not found: <path>". Programmatic users catching FileNotFoundError specifically will need to catch Error instead (#3051)SchemaFetchError instead of propagating raw exceptions - HTTP errors (4xx/5xx status codes), unexpected HTML responses, and transport errors (DNS, timeout, connection) that previously resulted in downstream YAML/JSON parse errors or raw httpx exceptions now raise SchemaFetchError (a subclass of Error) before parsing is attempted. Users catching specific parse errors or httpx exceptions for these scenarios will need to update their error handling (#3051)SchemaFetchError instead of raw httpx exceptions - The get_body() function in http.py now catches HTTP errors and raises SchemaFetchError (a new Error subclass) for HTTP status >= 400, network failures, and unexpected HTML responses. Code that caught raw httpx exceptions from remote schema fetching will need to catch SchemaFetchError instead. (#3071)$ref fetching now emits FutureWarning without --allow-remote-refs - Fetching remote HTTP/HTTPS $ref references without explicitly passing --allow-remote-refs now emits a FutureWarning deprecation warning. In a future version, remote fetching will be disabled by default. Users relying on implicit remote ref fetching should add --allow-remote-refs to suppress the warning. (#3071)SchemaFetchError with validation of response content type - Previously, fetching a remote $ref that returned an HTML error page would silently pass the HTML through as schema content. Now it raises SchemaFetchError if the response has text/html content type or a 4xx/5xx status code. This may cause previously-silent failures to become loud errors. (#3072)$ref fetching now emits FutureWarning - When a $ref resolves to an HTTP(S) URL and --allow-remote-refs is not explicitly passed, the tool still fetches the remote reference but emits a FutureWarning. This may cause failures in environments running with -W error (warnings as errors) or strict warning filters. Pass --allow-remote-refs explicitly to suppress the warning (#3051)$ref fetching now emits a FutureWarning - When the parser encounters an HTTP/HTTPS $ref without --allow-remote-refs being explicitly set, a FutureWarning is emitted warning that remote fetching will be disabled by default in a future version. Pass --allow-remote-refs to silence the warning, or --no-allow-remote-refs to block remote fetching immediately. (#3070)fields guard - All six type alias templates (TypeAliasAnnotation.jinja2, TypeAliasType.jinja2, TypeStatement.jinja2, UnionTypeAliasAnnotation.jinja2, UnionTypeAliasType.jinja2, UnionTypeStatement.jinja2) now wrap the main body in {% if fields %}...{% else %} blocks that fall back to {{ base_class }} when no fields are present. Users with custom copies of these templates must add the same guard or handle the empty-fields case. (#3070)fields guard and base_class fallback - The built-in templates TypeAliasAnnotation.jinja2, TypeAliasType.jinja2, TypeStatement.jinja2, and their Union variants now wrap field access in {%- if fields %}...{%- else %} blocks with a base_class fallback for empty field lists. Users with custom templates derived from the old versions will need to add similar guards. (#3071)TypeAliasAnnotation.jinja2, TypeAliasType.jinja2, TypeStatement.jinja2, and their Union variants) now handle an empty fields list with a fallback to base_class - If you have custom copies of these templates, they need to be updated to include the new {%- if fields %}...{%- else %}...{%- endif %} branching logic. Without this update, custom templates may error when fields is empty. (#3072)Full Changelog: https://github.com/koxudaxi/datamodel-code-generator/compare/0.55.0...0.56.0
Users running datamodel-code-generator with Pydantic v1 installed must upgrade to Pydantic v2. The previously deprecated v1 compatibility layer has be…
datamodel-codegen without specifying --output-model-type now generates Pydantic v2 models (pydantic_v2.BaseModel) instead of Pydantic v1 models (pydantic.BaseModel). Users who depend on the previous default behavior must now explicitly specify --output-model-type pydantic.BaseModel to continue generating Pydantic v1 compatible code. (#3029)RootModel instead of __root__ fields, native union syntax (str | None) instead of Optional[str], and Pydantic v2 validator/serializer decorators. Existing code that consumes generated models may need updates to work with the new Pydantic v2 output format. (#3029)pydantic.BaseModel output model type has been completely removed. Generated code now only supports Pydantic v2 patterns including RootModel instead of __root__, model_rebuild() instead of update_forward_refs(), and model_config instead of class Config. Users generating Pydantic v1 models must migrate to v2 output. (#3031)pydantic/BaseModel.jinja2 → use pydantic_v2/BaseModel.jinja2pydantic/BaseModel_root.jinja2 → use pydantic_v2/RootModel.jinja2pydantic/Config.jinja2 → removed (v2 uses model_config dict)
(#3031)--output-model-type pydantic.BaseModel removed - The pydantic.BaseModel value for --output-model-type is no longer valid. Use pydantic_v2.BaseModel instead (now the default). (#3031)datamodel_code_generator.util: model_dump(), model_validate(), get_fields_set(), model_copy(). Use Pydantic v2 methods directly (obj.model_dump(), cls.model_validate(), etc.). (#3031)datamodel_code_generator.model.pydantic module removed - The entire Pydantic v1 model module including BaseModel, CustomRootType, DataModelField, DataTypeManager, and dump_resolve_reference_action has been removed. Use datamodel_code_generator.model.pydantic_v2 instead. (#3031)pydantic>=1.5 to pydantic>=2,<3. Users with pydantic v1 installed must upgrade to pydantic v2 before using datamodel-code-generator (#3027)datamodel_code_generator.util: get_pydantic_version(), is_pydantic_v2(), model_validator(), field_validator(), and ConfigDict. Users who imported these internal utilities directly must update their code to use pydantic's native APIs (#3027)datamodel_code_generator.pydantic_patch module - The entire pydantic compatibility patching module was removed. Any code importing from this module will fail (#3027)packaging dependency - The packaging library is no longer a dependency. Code that relied on it being transitively available should add it explicitly (#3027)ValidatedDefault by @keyz in https://github.com/koxudaxi/datamodel-code-generator/pull/3040Full Changelog: https://github.com/koxudaxi/datamodel-code-generator/compare/0.54.1...0.55.0
Add dismissible announce bar to docs site by @koxudaxi in https://github.com/koxudaxi/datamodel-code-generator/pull/3004
--use-annotated and --use-non-positive-negative-number-constrained-types by @torarvid in https://github.com/koxudaxi/datamodel-code-generator/pull/3015Full Changelog: https://github.com/koxudaxi/datamodel-code-generator/compare/0.54.0...0.54.1
Enum member names from oneOf/anyOf const constructs now use title field when provided - Previously, when creating enums from oneOf/anyOf constructs wi
title field when provided - Previously, when creating enums from oneOf/anyOf constructs with const values, the title field was incorrectly ignored and enum member names were generated using the pattern {type}_{value} (e.g., integer_200). Now, when a title is specified, it is correctly used as the enum member name (e.g., OK instead of integer_200). Users who have code depending on the previously generated enum member names will need to update their references. (#2975)
Before:class StatusCode(IntEnum):
integer_200 = 200
integer_404 = 404
integer_500 = 500
After:class StatusCode(IntEnum):
OK = 200
Not_Found = 404
Server_Error = 500
int: int, list: list[str], dict: dict[str, Any]), the field is now renamed with a trailing underscore (e.g., int_) and an alias is added to preserve the original JSON field name. This prevents Python syntax issues and shadowing of builtin types. Previously, such fields were generated as-is (e.g., int: int | None = None), which could cause code that shadows Python builtins. After this change, the same field becomes int_: int | None = Field(None, alias='int'). This affects fields named: int, float, bool, str, bytes, list, dict, set, frozenset, tuple, and other Python builtins when their type annotation uses the matching builtin type. (#2968)$ref was combined with non-standard fields like markdownDescription, if, then, else, or other extras not in the whitelist, the generator would merge schemas and potentially create duplicate models (e.g., UserWithExtra alongside User). Now, only whitelisted schema-affecting extras (currently just const) trigger merging. This means:
properties:
user:
$ref: "#/definitions/User"
nullable: true
markdownDescription: "A user object"
Before: Could generate a merged UserWithMarkdownDescription model
After: Directly uses User | None reference (#2993)--capitalise-enum-members - Previously, enum values like replace, count, index would generate REPLACE_, COUNT_, INDEX_ when using --capitalise-enum-members. Now they correctly generate REPLACE, COUNT, INDEX. The underscore suffix is only added when --use-subclass-enum is also used AND the lowercase name conflicts with builtin type methods. Users relying on the previous naming (e.g., referencing MyEnum.REPLACE_ in code) will need to update to use the new names without trailing underscores. (#2999)$ref with inline keywords now include merged metadata - When a schema property uses $ref alongside additional keywords (e.g., const, enum, readOnly, constraints), the generator now correctly merges metadata (description, title, constraints, defaults, readonly/writeOnly) from the referenced schema into the field definition. Previously, this metadata was lost. For example, a field like type: Type may now become type: Type = Field(..., description='Type of this object.', title='type') when the referenced schema includes those attributes. This also affects additionalProperties and OpenAPI parameter schemas. (#2997)allOf's or anyOf's objects by @ilovelinux in https://github.com/koxudaxi/datamodel-code-generator/pull/2975Full Changelog: https://github.com/koxudaxi/datamodel-code-generator/compare/0.53.0...0.54.0
Parser subclass signature change - The Parser base class now requires two generic type parameters: Parser[ParserConfigT, SchemaFeaturesT] instead of j
Parser base class now requires two generic type parameters: Parser[ParserConfigT, SchemaFeaturesT] instead of just Parser[ParserConfigT]. Custom parser subclasses must be updated to include the second type parameter. (#2929)# Before
class MyCustomParser(Parser["MyParserConfig"]):
...
# After
class MyCustomParser(Parser["MyParserConfig", "JsonSchemaFeatures"]):
...
schema_features property required - Custom parser subclasses must now implement the schema_features abstract property that returns a JsonSchemaFeatures (or subclass) instance. (#2929)from functools import cached_property
from datamodel_code_generator.parser.schema_version import JsonSchemaFeatures
from datamodel_code_generator.enums import JsonSchemaVersion
class MyCustomParser(Parser["MyParserConfig", "JsonSchemaFeatures"]):
@cached_property
def schema_features(self) -> JsonSchemaFeatures:
return JsonSchemaFeatures.from_version(JsonSchemaVersion.Draft202012)
_create_default_config refactored to use class variable - Subclasses that override _create_default_config should now set the _config_class_name class variable instead. The base implementation uses this variable to dynamically instantiate the correct config class. (#2929)# Before
@classmethod
def _create_default_config(cls, options: MyConfigDict) -> MyParserConfig:
# custom implementation...
# After
_config_class_name: ClassVar[str] = "MyParserConfig"
# No need to override _create_default_config if using standard config creation
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. (#2960)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. (#2960)default_factory - Previously, required fields with list-type defaults (like __root__: list[ID] = ['abc', 'efg']) were generated with direct list assignments. Now they correctly use Field(default_factory=lambda: ...) which follows Python best practices for mutable defaults. This changes the structure of generated code for root models and similar patterns with list defaults. (#2958)
Before:class Family(BaseModel):
__root__: list[ID] = ['abc', 'efg']
After:class Family(BaseModel):
__root__: list[ID] = Field(
default_factory=lambda: [ID.parse_obj(v) for v in ['abc', 'efg']]
)
Full Changelog: https://github.com/koxudaxi/datamodel-code-generator/compare/0.52.2...0.53.0
Add support for multiple base classes in base_class_map and customBasePath by @koxudaxi in https://github.com/koxudaxi/datamodel-code-generator/pull/2
Full Changelog: https://github.com/koxudaxi/datamodel-code-generator/compare/0.52.1...0.52.2
Add deprecation warning for default output-model-type by @koxudaxi in https://github.com/koxudaxi/datamodel-code-generator/pull/2910
Full Changelog: https://github.com/koxudaxi/datamodel-code-generator/compare/0.52.0...0.52.1
Union fields with titles now wrapped in named models when --use-title-as-name is enabled - Previously, union-typed fields with a title were generated
--use-title-as-name is enabled - Previously, union-typed fields with a title were generated as inline union types (e.g., TypeA | TypeB | TypeC | None). Now they generate a separate wrapper model using the title name, and the field references this wrapper type (e.g., ProcessingStatusUnionTitle | None). This affects code that directly accesses union field values, as they now need to access the .root attribute (Pydantic v2) or .__root__ (Pydantic v1) of the wrapper model. (#2889)
Before:class ProcessingTaskTitle(BaseModel):
processing_status_union: (
ProcessingStatusDetail | ExtendedProcessingTask | ProcessingStatusTitle | None
) = Field('COMPLETED', title='Processing Status Union Title')
After:class ProcessingStatusUnionTitle(BaseModel):
__root__: (
ProcessingStatusDetail | ExtendedProcessingTask | ProcessingStatusTitle
) = Field(..., title='Processing Status Union Title')
class ProcessingTaskTitle(BaseModel):
processing_status_union: ProcessingStatusUnionTitle | None = Field(
default_factory=lambda: ProcessingStatusUnionTitle.parse_obj('COMPLETED'),
title='Processing Status Union Title',
)
--use-title-as-name is enabled - Arrays, dicts, enums-as-literals, and oneOf/anyOf unions that have a title in the schema now generate named type aliases or RootModel classes instead of being inlined. This improves readability but changes the generated type structure. For TypedDict output, generates type MyArrayName = list[str]. For Pydantic output, generates class MyArrayName(RootModel[list[str]]). (#2889)default_factory with a lambda that calls parse_obj() (Pydantic v1) or model_validate() (Pydantic v2) to construct the wrapper model. Code that introspects field defaults will see a factory function instead of a direct value. (#2889)$ref with nullable: true - When a JSON Schema property has a $ref combined with only nullable: true (and optionally metadata like title/description), the generator now uses the referenced type directly with Optional annotation instead of creating a new merged model. For example, a schema with multiple properties referencing User with nullable: true will now generate user_a: User | None instead of creating separate UserA, UserB model classes. This is a bug fix that reduces redundant model generation, but existing code that depends on the previously generated class names will break. (#2890)
Before:class UserA(BaseModel):
name: str
class UserB(BaseModel):
name: str
class Model(BaseModel):
user_a: UserA | None = None
user_b: UserB | None = None
After:class User(BaseModel):
name: str
class Model(BaseModel):
user_a: User | None = None
user_b: User | None = None
--use-title-as-name - When using --use-title-as-name, the generator now creates type aliases for additional cases: nested array items with titles, additionalProperties values with titles, oneOf/anyOf branches with titles, patternProperties, propertyNames, and primitive types with titles. Previously these were inlined; now they generate named type aliases. This is a bug fix per #2887, but changes generated output for schemas with titles on nested elements. (#2891)title is now excluded when merging with child schemas. This prevents unintended title inheritance that could affect model naming when --use-title-as-name is enabled. (#2891)allOf with single $ref no longer creates wrapper class - When a schema property uses allOf with only a single $ref and no additional properties, the generator now directly references the target type instead of creating an unnecessary wrapper class. This may affect code that depends on the previously generated wrapper class names or structure. For example, a property defined as allOf: [$ref: '#/components/schemas/ACHClass'] will now generate ach_class: ACHClass | None instead of creating an intermediate wrapper type. (#2902)Full Changelog: https://github.com/koxudaxi/datamodel-code-generator/compare/0.51.0...0.52.0
Add deprecation warning for Pydantic v1 runtime by @koxudaxi in https://github.com/koxudaxi/datamodel-code-generator/pull/2833
--input-model with Set, FrozenSet, Mapping, or Sequence types - When using --input-model to convert Pydantic models or dataclasses, types that were previously converted to list or dict are now preserved as their original Python types. For example, a field typed as Set[str] now generates set[str] instead of list[str], FrozenSet[T] generates frozenset[T], Mapping[K, V] generates Mapping[K, V] instead of dict[K, V], and Sequence[T] generates Sequence[T] instead of list[T]. This may cause type checking differences or runtime behavior changes if your code depended on the previous output types. (#2837)allOf with multiple $ref items where the child schema also defines properties that override parent properties will now generate classes with multiple inheritance (e.g., class Person(Thing, Location)) instead of a flattened single class with all properties merged inline. Previously, child property overrides were incorrectly treated as conflicts, triggering schema merging. Users relying on the flattened output may need to adjust their code. (#2838)
Before:class Person(BaseModel):
type: str | None = 'playground:Person'
name: constr(min_length=1) | None = None
address: constr(min_length=5)
age: int | None = None
After:class Thing(BaseModel):
type: str
name: constr(min_length=1)
class Location(BaseModel):
address: constr(min_length=5)
class Person(Thing, Location):
type: str | None = 'playground:Person'
name: constr(min_length=1) | None = None
age: int | None = None
ruff-check formatter, the --unsafe-fixes flag is now passed to ruff, which enables fixes that may change code behavior in potentially incorrect ways. This includes removing unused imports that might have side effects, removing unused variables that could affect debugging, and other transformations ruff considers "unsafe". Users who relied on the previous conservative safe-only fix behavior may see different generated code output. To restore the previous behavior, users can configure ruff via pyproject.toml or ruff.toml to disable specific unsafe rules. (#2847)--reuse-model (Pydantic v2 only), models that would previously generate as type aliases (ChildModel = ParentModel) now generate as explicit subclasses (class ChildModel(ParentModel): pass). This change improves type checker compatibility and maintains proper type identity, but may affect code that relied on type alias semantics or compared types directly. (#2853)
Before:ArmLeft = ArmRight
After:class ArmLeft(ArmRight):
pass
const values in anyOf/oneOf now generate Literal types instead of inferred base types - Previously, a const value like "MODE_2D" in an anyOf/oneOf schema would generate str type. Now it generates Literal["MODE_2D"]. This change affects type hints in generated models and may require updates to code that type-checks against the generated output. For example:# Before (v0.x)
map_view_mode: str = Field("MODE_2D", alias="mapViewMode", const=True)
apiVersion: str = Field('v1', const=True)
# After (this PR)
map_view_mode: Literal["MODE_2D"] = Field("MODE_2D", alias="mapViewMode", const=True)
apiVersion: Literal['v1'] = Field('v1', const=True)
This is a bug fix that makes the generated code more type-safe, but downstream code performing type comparisons or using isinstance(field, str) checks may need adjustment. (#2864)DataType class: is_frozen_set, is_mapping, and is_sequence. Custom Jinja2 templates that inspect DataType flags may need to be updated to handle these new type variations if they contain logic that depends on exhaustive type flag checks. (#2837)pydantic_v2/BaseModel.jinja2 template, you need to update it. The conditional block that generated type aliases ({% if base_class != "BaseModel" and ... %}{{ class_name }} = {{ base_class }}{% else %}...{% endif %}) has been removed. Templates should now always generate class declarations. (#2853)--input-model-ref-strategy reuse-foreign behavior changed - Previously, this strategy compared the source type family against the input model's family (e.g., if input was Pydantic, any non-Pydantic type like dataclass was considered "foreign" and reused). Now it compares against the output model's family. This means types that were previously imported/reused may now be regenerated, and vice versa. For example, when converting a Pydantic model containing a dataclass to TypedDict output, the dataclass was previously imported (it was "foreign" to Pydantic input), but now it will be regenerated (it's not the same family as TypedDict output). Enums are always reused regardless of output type. (#2854)generate() allowed passing both a config object and individual keyword arguments, with keyword arguments overriding config values. Now, providing both raises ValueError: "Cannot specify both 'config' and keyword arguments. Use one or the other." Users must choose one approach: either pass a GenerateConfig object or use keyword arguments, but not both. (#2874)# Before (worked): keyword args overrode config values
generate(input_=schema, config=config, output=some_path)
# After (raises ValueError): must use one or the other
# Option 1: Use config only (include output in config)
config = GenerateConfig(output=some_path, ...)
generate(input_=schema, config=config)
# Option 2: Use keyword args only
generate(input_=schema, output=some_path, ...)
Parser.__init__, JsonSchemaParser.__init__, OpenAPIParser.__init__, and GraphQLParser.__init__ now accept either a config: ParserConfig object OR keyword arguments via **options: Unpack[ParserConfigDict], but not both simultaneously. Passing both raises a ValueError. Existing code using only keyword arguments continues to work unchanged. (#2877)# Before: Could potentially mix config with kwargs (undefined behavior)
parser = JsonSchemaParser(source="{}", config=some_config, field_constraints=True)
# After: Raises ValueError - must use one approach or the other
parser = JsonSchemaParser(source="{}", config=some_config) # Use config object
# OR
parser = JsonSchemaParser(source="{}", field_constraints=True) # Use keyword args
Parser, JsonSchemaParser, OpenAPIParser, or GraphQLParser may need updates if they override __init__ and call super().__init__() with explicit parameter lists. The new signature uses **options: Unpack[ParserConfigDict] instead of explicit parameters. (#2877)Config.input_model type changed from str to list[str] - The input_model field in the Config class now stores a list of strings instead of a single string. While backward compatibility is maintained when setting the value (single strings are automatically coerced to lists), code that reads config.input_model will now receive a list[str] instead of str | None. Users who programmatically access this field should update their code to handle the list type. (#2881)# Before
if config.input_model:
process_model(config.input_model) # config.input_model was str
# After
if config.input_model:
for model in config.input_model: # config.input_model is now list[str]
process_model(model)
Full Changelog: https://github.com/koxudaxi/datamodel-code-generator/compare/0.50.0...0.51.0
Models with unevaluatedProperties now generate extra field configuration - JSON Schemas containing unevaluatedProperties: false will now generate mode
unevaluatedProperties now generate extra field configuration - JSON Schemas containing unevaluatedProperties: false will now generate models with extra='forbid' (Pydantic v2) or extra = Extra.forbid (Pydantic v1), and schemas with unevaluatedProperties: true will generate extra='allow'. Previously this keyword was ignored. This may cause validation errors for data that was previously accepted. (#2797)
Example - a schema like:{
"title": "Resource",
"type": "object",
"properties": { "name": { "type": "string" } },
"unevaluatedProperties": false
}
Previously generated:class Resource(BaseModel):
name: str | None = None
Now generates:class Resource(BaseModel):
model_config = ConfigDict(extra='forbid')
name: str | None = None
utf-8 instead of the system's locale-preferred encoding (e.g., cp1252 on Windows). Users who rely on locale-specific encoding must now explicitly use --encoding to specify their desired encoding (#2802)Full Changelog: https://github.com/koxudaxi/datamodel-code-generator/compare/0.49.0...0.50.0
Merge duplicate breaking change headings in release notes by @koxudaxi in https://github.com/koxudaxi/datamodel-code-generator/pull/2776
SchemaParseError instead of Pydantic ValidationError - When parsing invalid schema data (e.g., "minimum": "not_a_number"), the library now raises SchemaParseError instead of passing through Pydantic's ValidationError. Users catching pydantic.ValidationError for schema validation failures should update to catch SchemaParseError. The original error is preserved in the original_error attribute. (#2786)--output is not specified - The --output argument has always documented (default: stdout) in --help, but previously no output was produced. Now works as documented. (#2787)generate() function now returns str | GeneratedModules | None instead of None - Existing code ignoring the return value is unaffected. (#2787)Full Changelog: https://github.com/koxudaxi/datamodel-code-generator/compare/0.48.0...0.49.0
Non-lowercase forms (True, False, TRUE, FALSE) now emit a deprecation warning indicating future versions will only support lowercase true/false.
Custom class name generator now applied consistently during duplicate name resolution - Previously, when using custom_class_name_generator, the default PascalCase naming was incorrectly applied during duplicate name resolution. Now the custom generator is respected throughout, which may change generated class names. For example, a class name like nested_object_result with a custom generator f"Custom{name}" will now produce CustomNested_object_result instead of CustomNestedObjectResult. Users relying on the previous behavior should update their code to expect the new, correct class names. (#2757)
YAML 1.1 boolean keywords now preserved as strings in enums - Values like YES, NO, on, off, y, n that were previously converted to Python booleans are now preserved as their original string values. This fixes issues where string enum values were incorrectly converted but may change generated output for schemas that relied on the previous behavior. For example, a YAML enum with YES will now generate YES = 'YES' instead of being converted to True. (#2767)
true, false, True, False, TRUE, FALSE are now recognized as boolean values. YAML 1.1 boolean aliases (yes, no, on, off, y, n, etc.) are no longer parsed as booleans and will be treated as strings. Non-lowercase forms (True, False, TRUE, FALSE) now emit a deprecation warning indicating future versions will only support lowercase true/false. (#2767)Full Changelog: https://github.com/koxudaxi/datamodel-code-generator/compare/0.47.0...0.48.0
Add Python 3.13 deprecation warning documentation by @koxudaxi in https://github.com/koxudaxi/datamodel-code-generator/pull/2743
RootModel defaults use direct instantiation - RootModel fields with default values now generate ClassName(value) instead of ClassName.model_validate(value). This produces cleaner code but changes the generated output (#2714)
--strict-nullable now applies to JSON Schema - The --strict-nullable option is no longer OpenAPI-only and has been moved to Field customization options. It now also correctly respects nullable on array items (#2713, #2727)
If you use custom Jinja2 templates that check field.nullable, you may need to update them. The nullable field on JsonSchemaObject now defaults to None instead of False. Templates should check field.nullable is True instead of just if field.nullable (#2715)
Example change:
{# Before #}
{%- if field.nullable %}...{% endif %}
{# After #}
{%- if field.nullable is true %}...{% endif %}
Full Changelog: https://github.com/koxudaxi/datamodel-code-generator/compare/0.46.0...0.47.0
Python 3.9 support dropped - Minimum Python version is now 3.10+
list[...]/dict[...] are now used instead of List[...]/Dict[...]. Use --no-use-standard-collections to restore previous behavior (#2699)X | Y syntax is now used instead of Union[X, Y]/Optional[X]. Use --no-use-union-operator to restore previous behavior (#2703)required and nullable no longer get = None. The pydantic_v2/BaseModel.jinja2 template logic was updated to only assign default values when field.required is false. This fixes incorrect behavior where required fields could be omitted (#2520)--enum-field-as-literal setting instead of forcing all. Added new --enum-field-as-literal none option (#2691)prefixItems with matching minItems/maxItems and no items now generates tuple[T1, T2, ...] instead of list[...]. This applies when the array has a fixed length with heterogeneous types (#2537)str | None) by @koxudaxi in https://github.com/koxudaxi/datamodel-code-generator/pull/2709Full Changelog: https://github.com/koxudaxi/datamodel-code-generator/compare/0.45.0...0.46.0
docs: add cross-links between CLI reference and usage documentation pages by @koxudaxi in https://github.com/koxudaxi/datamodel-code-generator/pull/26
Full Changelog: https://github.com/koxudaxi/datamodel-code-generator/compare/0.44.0...0.45.0
Fix empty dict/list defaults not generating default_factory for Pydantic models by @koxudaxi in https://github.com/koxudaxi/datamodel-code-generator/p
from __future__ import annotations for Python 3.14+ targets (PEP 649) by @koxudaxi in https://github.com/koxudaxi/datamodel-code-generator/pull/2658Full Changelog: https://github.com/koxudaxi/datamodel-code-generator/compare/0.43.1...0.44.0
Your coding agent can read these notes before it upgrades. Set up the MCP server →