@@ -247,6 +247,16 @@ async def orm_get_obj(self, id: UUID | int | str) -> Any | None:
247247 """
248248 raise NotImplementedError
249249
250+ async def orm_serialize_obj_by_id (self , id : UUID | int | str ) -> dict | None :
251+ """Serialize object by id (e.g. in a single session to avoid DetachedInstanceError).
252+
253+ Override in ORM layer to run get+serialize inside one session when needed.
254+ """
255+ obj = await self .orm_get_obj (id )
256+ if obj is None :
257+ return None
258+ return await self .serialize_obj (obj )
259+
250260 async def orm_save_obj (self , id : UUID | Any | None , payload : dict ) -> Any :
251261 """This method is used to save orm/db model object.
252262
@@ -361,6 +371,23 @@ async def serialize_obj_attributes(
361371 serialized_dict ["__str__" ] = await str_fn ()
362372 return serialized_dict
363373
374+ async def _serialize_obj_after_save (self , obj : Any ) -> dict :
375+ """Serialize object after save; re-fetch if detached (e.g. SQLAlchemy after commit)."""
376+ try :
377+ return await self .serialize_obj (obj )
378+ except Exception as exc :
379+ # SQLAlchemy DetachedInstanceError when session is closed after commit
380+ if exc .__class__ .__name__ != "DetachedInstanceError" :
381+ raise
382+ pk_name = self .get_model_pk_name (self .model_cls )
383+ pk = getattr (obj , pk_name , None )
384+ if pk is None :
385+ raise
386+ result = await self .orm_serialize_obj_by_id (pk )
387+ if result is None :
388+ raise
389+ return result
390+
364391 async def serialize_obj (self , obj : Any , list_view : bool = False ) -> dict :
365392 """Serialize orm model obj to dict.
366393
@@ -404,8 +431,13 @@ def deserialize_value(self, field: ModelFieldWidgetSchema, value: Any) -> Any:
404431 return value
405432 match field .form_widget_type :
406433 case WidgetType .TimePicker :
407- return datetime .datetime .fromisoformat (value ).time ()
408- case WidgetType .DatePicker | WidgetType .DateTimePicker :
434+ try :
435+ return datetime .datetime .fromisoformat (value ).time ()
436+ except ValueError :
437+ return datetime .time .fromisoformat (value )
438+ case WidgetType .DatePicker :
439+ return datetime .datetime .fromisoformat (value ).date ()
440+ case WidgetType .DateTimePicker :
409441 return datetime .datetime .fromisoformat (value )
410442 case _:
411443 return value
@@ -483,7 +515,7 @@ async def save_model(self, id: UUID | int | str | None, payload: dict) -> dict |
483515 if m2m_field .name in payload :
484516 await self .orm_save_m2m_ids (obj , m2m_field .column_name , payload [m2m_field .name ])
485517
486- return await self .serialize_obj (obj )
518+ return await self ._serialize_obj_after_save (obj )
487519
488520 async def delete_model (self , id : UUID | int | str ) -> None :
489521 """This method is used to delete orm/db model object.
0 commit comments