@@ -924,7 +924,9 @@ def __setattr__(self, name: str, value: Any) -> None:
924924 and name in self .__sqlmodel_relationships__
925925 and value is not None
926926 ):
927- value = _convert_pydantic_to_table_model (value , name , self .__class__ )
927+ value = _convert_pydantic_to_table_model (
928+ value , name , self .__class__ , self
929+ )
928930
929931 # Set in SQLAlchemy, before Pydantic to trigger events and updates
930932 if is_table_model_class (self .__class__ ) and is_instrumented (self , name ): # type: ignore[no-untyped-call]
@@ -1127,7 +1129,10 @@ def sqlmodel_update(
11271129
11281130
11291131def _convert_pydantic_to_table_model (
1130- value : Any , relationship_name : str , owner_class : Type ["SQLModel" ]
1132+ value : Any ,
1133+ relationship_name : str ,
1134+ owner_class : Type ["SQLModel" ],
1135+ instance : Optional ["SQLModel" ] = None ,
11311136) -> Any :
11321137 """
11331138 Convert Pydantic objects to table models for relationship assignments.
@@ -1136,6 +1141,7 @@ def _convert_pydantic_to_table_model(
11361141 value: The value being assigned to the relationship
11371142 relationship_name: Name of the relationship attribute
11381143 owner_class: The class that owns the relationship
1144+ instance: The SQLModel instance (for session context)
11391145
11401146 Returns:
11411147 Converted value(s) - table model instances instead of Pydantic objects
@@ -1185,7 +1191,7 @@ def _convert_pydantic_to_table_model(
11851191 converted_items = []
11861192 for item in value :
11871193 converted_item = _convert_single_pydantic_to_table_model (
1188- item , target_type
1194+ item , target_type , instance
11891195 )
11901196 converted_items .append (converted_item )
11911197 return converted_items
@@ -1198,21 +1204,24 @@ def _convert_pydantic_to_table_model(
11981204 resolved_type = default_registry ._class_registry .get (target_type )
11991205 if resolved_type is not None :
12001206 target_type = resolved_type
1201- except :
1207+ except Exception :
12021208 pass
12031209
1204- return _convert_single_pydantic_to_table_model (value , target_type )
1210+ return _convert_single_pydantic_to_table_model (value , target_type , instance )
12051211
12061212 return value
12071213
12081214
1209- def _convert_single_pydantic_to_table_model (item : Any , target_type : Any ) -> Any :
1215+ def _convert_single_pydantic_to_table_model (
1216+ item : Any , target_type : Any , instance : Optional ["SQLModel" ] = None
1217+ ) -> Any :
12101218 """
12111219 Convert a single Pydantic object to a table model.
12121220
12131221 Args:
12141222 item: The Pydantic object to convert
12151223 target_type: The target table model type
1224+ instance: The SQLModel instance (for session context)
12161225
12171226 Returns:
12181227 Converted table model instance or original item if no conversion needed
@@ -1226,7 +1235,9 @@ def _convert_single_pydantic_to_table_model(item: Any, target_type: Any) -> Any:
12261235 try :
12271236 # Attempt to resolve forward reference from the default registry
12281237 # This was part of the original logic and should be kept
1229- resolved_type_from_registry = default_registry ._class_registry .get (target_type )
1238+ resolved_type_from_registry = default_registry ._class_registry .get (
1239+ target_type
1240+ )
12301241 if resolved_type_from_registry is not None :
12311242 resolved_target_type = resolved_type_from_registry
12321243 except Exception :
@@ -1235,9 +1246,14 @@ def _convert_single_pydantic_to_table_model(item: Any, target_type: Any) -> Any:
12351246 # `_convert_pydantic_to_table_model` should provide a resolved type.
12361247 # For safety, if it's still a string here, and item is a simple Pydantic model,
12371248 # it's best to return item to avoid errors if no concrete type is found.
1238- if isinstance (resolved_target_type , str ) and isinstance (item , BaseModel ) and hasattr (item , "__class__" ) and not is_table_model_class (item .__class__ ):
1239- return item # Fallback if no concrete type can be determined
1240- pass # Continue if resolved_target_type is now a class or item is not a simple Pydantic model
1249+ if (
1250+ isinstance (resolved_target_type , str )
1251+ and isinstance (item , BaseModel )
1252+ and hasattr (item , "__class__" )
1253+ and not is_table_model_class (item .__class__ )
1254+ ):
1255+ return item # Fallback if no concrete type can be determined
1256+ pass # Continue if resolved_target_type is now a class or item is not a simple Pydantic model
12411257
12421258 # If resolved_target_type is still a string and not a class, we cannot proceed with conversion.
12431259 # This can happen if the forward reference cannot be resolved.
@@ -1253,7 +1269,8 @@ def _convert_single_pydantic_to_table_model(item: Any, target_type: Any) -> Any:
12531269 if not (
12541270 hasattr (resolved_target_type , "__mro__" )
12551271 and any (
1256- hasattr (cls , "__sqlmodel_relationships__" ) for cls in resolved_target_type .__mro__
1272+ hasattr (cls , "__sqlmodel_relationships__" )
1273+ for cls in resolved_target_type .__mro__
12571274 )
12581275 ):
12591276 return item
@@ -1278,10 +1295,49 @@ def _convert_single_pydantic_to_table_model(item: Any, target_type: Any) -> Any:
12781295 # Pydantic v1
12791296 data = item .dict ()
12801297
1298+ # If instance is available and item has an ID, try to find existing record
1299+ if instance is not None and "id" in data and data ["id" ] is not None :
1300+ from sqlalchemy .orm import object_session
1301+
1302+ session = object_session (instance )
1303+ if session is not None :
1304+ # Try to find existing record by ID
1305+ existing_record = session .get (resolved_target_type , data ["id" ])
1306+ if existing_record is not None :
1307+ # Update existing record with new data
1308+ for key , value in data .items ():
1309+ if key != "id" and hasattr (existing_record , key ):
1310+ setattr (existing_record , key , value )
1311+ return existing_record
1312+
12811313 # Create new table model instance using resolved_target_type
12821314 return resolved_target_type (** data )
12831315 except Exception :
12841316 # If conversion fails, return original item
12851317 return item
12861318
1319+ # Check if item is a dictionary that should be converted to table model
1320+ elif isinstance (item , dict ):
1321+ try :
1322+ # If instance is available and item has an ID, try to find existing record
1323+ if instance is not None and "id" in item and item ["id" ] is not None :
1324+ from sqlalchemy .orm import object_session
1325+
1326+ session = object_session (instance )
1327+ if session is not None :
1328+ # Try to find existing record by ID
1329+ existing_record = session .get (resolved_target_type , item ["id" ])
1330+ if existing_record is not None :
1331+ # Update existing record with new data
1332+ for key , value in item .items ():
1333+ if key != "id" and hasattr (existing_record , key ):
1334+ setattr (existing_record , key , value )
1335+ return existing_record
1336+
1337+ # Create new table model instance from dictionary
1338+ return resolved_target_type (** item )
1339+ except Exception :
1340+ # If conversion fails, return original item
1341+ return item
1342+
12871343 return item
0 commit comments