@@ -477,6 +477,27 @@ def has_ref_with_schema_keywords(self) -> bool:
477477 schema_affecting_fields |= {"extras" }
478478 return bool (schema_affecting_fields )
479479
480+ @cached_property
481+ def is_ref_with_nullable_only (self ) -> bool :
482+ """Check if schema has $ref with only nullable: true (no other schema-affecting keywords).
483+
484+ This is used to avoid creating duplicate models when a $ref is combined
485+ with nullable: true. In such cases, the reference should be used directly
486+ with Optional type annotation instead of merging schemas.
487+ """
488+ if not self .ref or self .nullable is not True :
489+ return False
490+ other_fields = get_fields_set (self ) - {"ref" , "nullable" } - self .__metadata_only_fields__ - {"extras" }
491+ if other_fields :
492+ return False
493+ if self .extras :
494+ schema_affecting_extras = {
495+ k for k in self .extras if k not in self .__metadata_only_fields__ and not k .startswith ("x-" )
496+ }
497+ if schema_affecting_extras :
498+ return False
499+ return True
500+
480501
481502@lru_cache
482503def get_ref_type (ref : str ) -> JSONReference :
@@ -1805,7 +1826,7 @@ def _handle_allof_root_model_with_constraints( # noqa: PLR0911, PLR0912
18051826 if ref_value is None :
18061827 return None # pragma: no cover
18071828
1808- if ref_item .has_ref_with_schema_keywords :
1829+ if ref_item .has_ref_with_schema_keywords and not ref_item . is_ref_with_nullable_only :
18091830 ref_schema = self ._merge_ref_with_schema (ref_item )
18101831 else :
18111832 ref_schema = self ._load_ref_schema_object (ref_value )
@@ -1896,7 +1917,7 @@ def parse_combined_schema(
18961917 refs = []
18971918 for index , target_attribute in enumerate (getattr (obj , target_attribute_name , [])):
18981919 if target_attribute .ref :
1899- if target_attribute .has_ref_with_schema_keywords :
1920+ if target_attribute .has_ref_with_schema_keywords and not target_attribute . is_ref_with_nullable_only :
19001921 merged_attr = self ._merge_ref_with_schema (target_attribute )
19011922 combined_schemas .append (
19021923 model_validate (
@@ -2700,6 +2721,11 @@ def parse_item( # noqa: PLR0911, PLR0912, PLR0914
27002721 item ,
27012722 root_type_path ,
27022723 )
2724+ if item .is_ref_with_nullable_only and item .ref :
2725+ ref_data_type = self .get_ref_data_type (item .ref )
2726+ if self .strict_nullable :
2727+ return self .data_type (data_types = [ref_data_type ], is_optional = True )
2728+ return ref_data_type
27032729 if item .has_ref_with_schema_keywords :
27042730 item = self ._merge_ref_with_schema (item )
27052731 if item .ref :
@@ -3523,7 +3549,7 @@ def parse_obj( # noqa: PLR0912
35233549 path : list [str ],
35243550 ) -> None :
35253551 """Parse a JsonSchemaObject by dispatching to appropriate parse methods."""
3526- if obj .has_ref_with_schema_keywords :
3552+ if obj .has_ref_with_schema_keywords and not obj . is_ref_with_nullable_only :
35273553 obj = self ._merge_ref_with_schema (obj )
35283554
35293555 if obj .is_array :
0 commit comments