From ec2d2dbcaa4436354a24f9bc3e9ad93db4b7149b Mon Sep 17 00:00:00 2001 From: Iwantexpresso Date: Thu, 7 May 2026 13:15:16 -0500 Subject: [PATCH 1/2] attemtped fix on api null id --- specifyweb/specify/api/crud.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/specifyweb/specify/api/crud.py b/specifyweb/specify/api/crud.py index d0923d3b5d9..e7b98c7e8b1 100644 --- a/specifyweb/specify/api/crud.py +++ b/specifyweb/specify/api/crud.py @@ -156,8 +156,6 @@ def update_obj(collection, agent, name: str, id, version, data: dict[str, Any], def delete_obj(obj, deleter: Callable[[Any, Any], None] | None=None, version=None, parent_obj=None, clean_predelete=None) -> None: # need to delete dependent -to-one records - # e.g. delete CollectionObjectAttribute when CollectionObject is deleted - # but have to delete the referring record first dependents_to_delete = [_f for _f in ( get_related_or_none(obj, field.name) for field in obj._meta.get_fields() @@ -173,13 +171,15 @@ def delete_obj(obj, deleter: Callable[[Any, Any], None] | None=None, version=Non if hasattr(obj, 'pre_constraints_delete'): obj.pre_constraints_delete() - if deleter: + # CRITICAL: Only call deleter if object still has an ID + # Dependent objects may already be cascade-deleted with id=None + if deleter and obj.id is not None: deleter(obj, parent_obj) obj.delete() - + for dep in dependents_to_delete: - delete_obj(dep, deleter, version, parent_obj=obj, clean_predelete=clean_predelete) + delete_obj(dep, deleter, version, parent_obj=obj, clean_predelete=clean_predelete) def update_or_create_resource(collection, agent, model, data, parent_obj, parent_relationship=None): if 'id' in data: From 559c66c8fc7885a0f4ceca1f9c091489e3a77eff Mon Sep 17 00:00:00 2001 From: Iwantexpresso Date: Mon, 11 May 2026 12:50:03 -0500 Subject: [PATCH 2/2] sotred obj,ids before deletion to bouded ID being Set to none during Cascade deletion --- specifyweb/specify/api/crud.py | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/specifyweb/specify/api/crud.py b/specifyweb/specify/api/crud.py index e7b98c7e8b1..c00205902cb 100644 --- a/specifyweb/specify/api/crud.py +++ b/specifyweb/specify/api/crud.py @@ -170,15 +170,18 @@ def delete_obj(obj, deleter: Callable[[Any, Any], None] | None=None, version=Non if hasattr(obj, 'pre_constraints_delete'): obj.pre_constraints_delete() + # store obj id and class before deletion to avoid accessing deleted obj + obj_id = obj.id - # CRITICAL: Only call deleter if object still has an ID - # Dependent objects may already be cascade-deleted with id=None - if deleter and obj.id is not None: + if deleter and (obj_id is not None): deleter(obj, parent_obj) obj.delete() - + +# before delete obj, store dep id to avoid accessing deleted obj in recursive delete calls for dep in dependents_to_delete: + dep_id = dep.id + if dep_id is not None: delete_obj(dep, deleter, version, parent_obj=obj, clean_predelete=clean_predelete) def update_or_create_resource(collection, agent, model, data, parent_obj, parent_relationship=None):