@@ -979,6 +979,117 @@ def _should_generate_base_model(self, *, generates_separate_models: bool = False
979979 return True
980980 return not generates_separate_models
981981
982+ def _ref_schema_generates_variant (self , ref_path : str , suffix : str ) -> bool :
983+ """Check if a referenced schema will generate a specific variant (Request or Response).
984+
985+ For Request variant: schema must have readOnly fields AND at least one non-readOnly field.
986+ For Response variant: schema must have writeOnly fields AND at least one non-writeOnly field.
987+ """
988+ try :
989+ ref_schema = self ._load_ref_schema_object (ref_path )
990+ except Exception : # noqa: BLE001 # pragma: no cover
991+ return False
992+
993+ # Collect all fields from the schema
994+ has_read_only = False
995+ has_write_only = False
996+ has_non_read_only = False
997+ has_non_write_only = False
998+
999+ if ref_schema .properties :
1000+ for prop in ref_schema .properties .values ():
1001+ if isinstance (prop , JsonSchemaObject ):
1002+ is_read_only = self ._resolve_field_flag (prop , "readOnly" )
1003+ is_write_only = self ._resolve_field_flag (prop , "writeOnly" )
1004+ if is_read_only :
1005+ has_read_only = True
1006+ else :
1007+ has_non_read_only = True
1008+ if is_write_only :
1009+ has_write_only = True
1010+ else :
1011+ has_non_write_only = True
1012+
1013+ # For Request variant: needs readOnly fields AND at least one non-readOnly field remaining
1014+ if suffix == "Request" :
1015+ return has_read_only and has_non_read_only
1016+ # For Response variant: needs writeOnly fields AND at least one non-writeOnly field remaining
1017+ if suffix == "Response" :
1018+ return has_write_only and has_non_write_only
1019+ return False # pragma: no cover - only Request and Response suffixes are used
1020+
1021+ def _ref_schema_has_model (self , ref_path : str ) -> bool :
1022+ """Check if a referenced schema will have a model (base or variant) generated.
1023+
1024+ Returns False if the schema has only readOnly or only writeOnly fields in request-response mode,
1025+ which would result in no model being generated at all.
1026+ """
1027+ try :
1028+ ref_schema = self ._load_ref_schema_object (ref_path )
1029+ except Exception : # noqa: BLE001 # pragma: no cover
1030+ return True # Assume it exists if we can't load it
1031+
1032+ # Check if the schema has any fields that aren't readOnly or writeOnly
1033+ has_read_only = False
1034+ has_write_only = False
1035+
1036+ if ref_schema .properties :
1037+ for prop in ref_schema .properties .values ():
1038+ if isinstance (prop , JsonSchemaObject ):
1039+ is_read_only = self ._resolve_field_flag (prop , "readOnly" )
1040+ is_write_only = self ._resolve_field_flag (prop , "writeOnly" )
1041+ if is_read_only :
1042+ has_read_only = True
1043+ elif is_write_only :
1044+ has_write_only = True
1045+ else : # pragma: no cover - plain fields in refs to non-variant schemas
1046+ # Has a plain field - schema will have a model
1047+ return True
1048+
1049+ # Has ONLY readOnly fields - no model in request-response mode
1050+ if has_read_only and not has_write_only :
1051+ return False
1052+ # Has ONLY writeOnly fields - no model, or has both/neither - has model
1053+ return not (has_write_only and not has_read_only )
1054+
1055+ def _update_data_type_ref_for_variant (self , data_type : DataType , suffix : str ) -> None :
1056+ """Recursively update data type references to point to variant models."""
1057+ if data_type .reference :
1058+ ref_path = data_type .reference .path
1059+ if self ._ref_schema_generates_variant (ref_path , suffix ):
1060+ # The referenced schema will have a variant model - update the ref
1061+ path_parts = ref_path .split ("/" )
1062+ base_name = path_parts [- 1 ]
1063+ variant_name = f"{ base_name } { suffix } "
1064+ unique_name = self .model_resolver .get_class_name (variant_name , unique = False ).name
1065+ # Create the variant path (e.g., "#/components/schemas/ChildRequest")
1066+ path_parts [- 1 ] = unique_name
1067+ variant_ref = self .model_resolver .add (path_parts , unique_name , class_name = True , unique = False )
1068+ data_type .reference = variant_ref
1069+ elif not self ._ref_schema_has_model (ref_path ):
1070+ # The referenced schema won't have any model - mark it for forced base model generation
1071+ if not hasattr (self , "_force_base_model_refs" ):
1072+ self ._force_base_model_refs : set [str ] = set ()
1073+ self ._force_base_model_refs .add (ref_path )
1074+ # Recursively update nested data types (for List[Child], Optional[Child], etc.)
1075+ for nested_dt in data_type .data_types :
1076+ self ._update_data_type_ref_for_variant (nested_dt , suffix )
1077+
1078+ def _update_field_refs_for_variant (
1079+ self , model_fields : list [DataModelFieldBase ], suffix : str
1080+ ) -> list [DataModelFieldBase ]:
1081+ """Update field references in model_fields to point to variant models.
1082+
1083+ For Request models, refs should point to Request variants.
1084+ For Response models, refs should point to Response variants.
1085+ """
1086+ if self .read_only_write_only_model_type != ReadOnlyWriteOnlyModelType .RequestResponse :
1087+ return model_fields
1088+ for field in model_fields :
1089+ if field .data_type :
1090+ self ._update_data_type_ref_for_variant (field .data_type , suffix )
1091+ return model_fields
1092+
9821093 def _create_variant_model ( # noqa: PLR0913, PLR0917
9831094 self ,
9841095 path : list [str ],
@@ -991,6 +1102,8 @@ def _create_variant_model( # noqa: PLR0913, PLR0917
9911102 """Create a Request or Response model variant."""
9921103 if not model_fields :
9931104 return
1105+ # Update field refs to point to variant models when in request-response mode
1106+ self ._update_field_refs_for_variant (model_fields , suffix )
9941107 variant_name = f"{ base_name } { suffix } "
9951108 unique_name = self .model_resolver .get_class_name (variant_name , unique = True ).name
9961109 model_path = [* path [:- 1 ], unique_name ]
0 commit comments