1010from django .db .utils import IntegrityError
1111from rest_framework .exceptions import ValidationError as ValidationError
1212
13+ from .change_log_buffer import snapshot_for_apply
1314from .common import NON_FIELD_ERRORS , Change , ChangeSet , ChangeSetException , ChangeSetResult , ChangeType , error_from_validation_error
14- from .matcher import find_existing_object , invalidate_find_obj_entry
15+ from .matcher import find_existing_object , invalidate_find_obj_entry , requires_pre_save_match
1516from .plugin_utils import get_object_type_model , legal_fields
1617from .profile import profiled
1718from .supported_models import get_serializer_for_model
@@ -78,6 +79,7 @@ def _try_find_and_update_existing_instance(data: dict, object_type: str, seriali
7879 try :
7980 instance = find_existing_object (data , object_type )
8081 if instance :
82+ snapshot_for_apply (instance )
8183 serializer = serializer_class (instance , data = data , partial = True , context = {"request" : request })
8284 serializer .is_valid (raise_exception = True )
8385 result = serializer .save ()
@@ -110,8 +112,13 @@ def _apply_change(data: dict, model_class: models.Model, change: Change, created
110112 # For component types that may be auto-created from e.g. DeviceType or ModuleType templates,
111113 # try to find existing object first before attempting to create.
112114 # This prevents duplicates when components are instantiated during Device/Module save()
115+ # The same find-first path also handles types whose logical match
116+ # criteria are not enforced by a DB unique constraint (see
117+ # matcher._REQUIRES_PRE_SAVE_MATCH): concurrent planners would
118+ # otherwise each emit CREATE for the same logical row and both
119+ # inserts would succeed without IntegrityError to fall back on.
113120 instance = None
114- if _is_auto_created_component (change .object_type ):
121+ if _is_auto_created_component (change .object_type ) or requires_pre_save_match ( change . object_type ) :
115122 instance = _try_find_and_update_existing_instance (data , change .object_type , serializer_class , request )
116123
117124 if not instance :
@@ -124,6 +131,7 @@ def _apply_change(data: dict, model_class: models.Model, change: Change, created
124131 elif change_type == ChangeType .UPDATE :
125132 if object_id := change .object_id :
126133 instance = model_class .objects .get (id = object_id )
134+ snapshot_for_apply (instance )
127135 serializer = serializer_class (instance , data = data , partial = True , context = {"request" : request })
128136 serializer .is_valid (raise_exception = True )
129137 serializer .save ()
0 commit comments