Breaking Changes
Code Generation Changes
- Union fields with titles now wrapped in named models when
--use-title-as-nameis enabled - Previously, union-typed fields with atitlewere 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.rootattribute (Pydantic v2) or.__root__(Pydantic v1) of the wrapper model. (#2889)
Before:After:class ProcessingTaskTitle(BaseModel): processing_status_union: ( ProcessingStatusDetail | ExtendedProcessingTask | ProcessingStatusTitle | None ) = Field('COMPLETED', title='Processing Status Union Title')
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', )
- Inline types with titles now generate named type aliases when
--use-title-as-nameis enabled - Arrays, dicts, enums-as-literals, and oneOf/anyOf unions that have atitlein 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, generatestype MyArrayName = list[str]. For Pydantic output, generatesclass MyArrayName(RootModel[list[str]]). (#2889) - Default value handling changed for wrapped union fields - Fields that previously had simple default values now use
default_factorywith a lambda that callsparse_obj()(Pydantic v1) ormodel_validate()(Pydantic v2) to construct the wrapper model. Code that introspects field defaults will see a factory function instead of a direct value. (#2889) - Different output for
$refwithnullable: true- When a JSON Schema property has a$refcombined with onlynullable: true(and optionally metadata liketitle/description), the generator now uses the referenced type directly withOptionalannotation instead of creating a new merged model. For example, a schema with multiple properties referencingUserwithnullable: truewill now generateuser_a: User | Noneinstead of creating separateUserA,UserBmodel 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:After:class UserA(BaseModel): name: str class UserB(BaseModel): name: str class Model(BaseModel): user_a: UserA | None = None user_b: UserB | None = None
class User(BaseModel): name: str class Model(BaseModel): user_a: User | None = None user_b: User | None = None
- Type alias generation expanded for
--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 no longer inherited in combined schemas - In anyOf/oneOf/allOf schemas, the parent schema's
titleis now excluded when merging with child schemas. This prevents unintended title inheritance that could affect model naming when--use-title-as-nameis enabled. (#2891) allOfwith single$refno longer creates wrapper class - When a schema property usesallOfwith only a single$refand 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 asallOf: [$ref: '#/components/schemas/ACHClass']will now generateach_class: ACHClass | Noneinstead of creating an intermediate wrapper type. (#2902)
What's Changed
- Add ULID and Email format documentation by @koxudaxi in #2886
- Add --class-name-prefix, --class-name-suffix, and --class-name-affix-scope options by @koxudaxi in #2885
- Use class-name-suffix for parser config TypedDicts by @koxudaxi in #2888
- Create type aliases for inline types with title when use-title-as-name is enabled by @koxudaxi in #2889
- Fix duplicate model generation for $ref with nullable by @koxudaxi in #2890
- Create type aliases for nested elements with titles when use-title-as-name is enabled by @koxudaxi in #2891
- Clarify --aliases help text to explain schema field becomes Pydantic alias by @koxudaxi in #2892
- Document external library import use case for --type-overrides by @koxudaxi in #2893
- Add documentation for reducing duplicate field types by @koxudaxi in #2896
- Add FutureWarning for upcoming ruff default formatters by @koxudaxi in #2895
- Add --openapi-include-paths option for path-based model filtering by @koxudaxi in #2894
- Add --graphql-no-typename option to exclude typename field by @koxudaxi in #2899
- Add --default-values CLI option for overriding field defaults by @koxudaxi in #2897
- Fix allOf with single ref creating unnecessary wrapper class by @koxudaxi in #2902
- Fix --reuse-model --collapse-reuse-models to deduplicate identical inline definitions by @koxudaxi in #2903
- Add --use-serialization-alias option for Pydantic v2 by @koxudaxi in #2905
- Fix Pydantic v2 discriminated unions in array fields by @koxudaxi in #2907
Full Changelog: 0.51.0...0.52.0