@@ -54,49 +54,67 @@ def apply_changeset(change_set: ChangeSet, request) -> ChangeSetResult:
5454 id = change_set .id ,
5555 )
5656
57+ def _is_auto_created_component (object_type : str ) -> bool :
58+ """Check if the object type is auto-created from templates."""
59+ auto_created_components = [
60+ "dcim.consoleport" ,
61+ "dcim.consoleserverport" ,
62+ "dcim.powerport" ,
63+ "dcim.poweroutlet" ,
64+ "dcim.interface" ,
65+ "dcim.rearport" ,
66+ "dcim.frontport" ,
67+ "dcim.modulebay" ,
68+ "dcim.devicebay" ,
69+ "dcim.inventoryitem" ,
70+ ]
71+ return object_type in auto_created_components
72+
73+
74+ def _try_find_and_update_existing (data : dict , object_type : str , serializer_class , request ):
75+ """Try to find existing auto-created object and update it."""
76+ try :
77+ instance = find_existing_object (data , object_type )
78+ if instance :
79+ serializer = serializer_class (instance , data = data , partial = True , context = {"request" : request })
80+ serializer .is_valid (raise_exception = True )
81+ return serializer .save ()
82+ except (ValueError , TypeError ) as e :
83+ logger .debug (f"Could not find existing { object_type } : { e } " )
84+ return None
85+
86+
87+ def _create_or_find_instance (data : dict , object_type : str , serializer_class , request ):
88+ """Create new instance or find existing one on conflict."""
89+ serializer = serializer_class (data = data , context = {"request" : request })
90+ try :
91+ serializer .is_valid (raise_exception = True )
92+ return serializer .save ()
93+ except ValidationError as e :
94+ instance = find_existing_object (data , object_type )
95+ if not instance :
96+ raise e
97+ return instance
98+
99+
57100def _apply_change (data : dict , model_class : models .Model , change : Change , created : dict , request ):
58101 serializer_class = get_serializer_for_model (model_class )
59102 change_type = change .change_type
103+
60104 if change_type == ChangeType .CREATE .value :
61105 # For component types that may be auto-created from e.g. DeviceType or ModuleType templates,
62106 # try to find existing object first before attempting to create.
63107 # This prevents duplicates when components are instantiated during Device/Module save()
64108 instance = None
65- auto_created_components = [
66- "dcim.consoleport" ,
67- "dcim.consoleserverport" ,
68- "dcim.powerport" ,
69- "dcim.poweroutlet" ,
70- "dcim.interface" ,
71- "dcim.rearport" ,
72- "dcim.frontport" ,
73- "dcim.modulebay" ,
74- "dcim.devicebay" ,
75- "dcim.inventoryitem" ,
76- ]
77- if change .object_type in auto_created_components :
78- try :
79- instance = find_existing_object (data , change .object_type )
80- # If found, update it with any additional data from the changeset
81- if instance :
82- serializer = serializer_class (instance , data = data , partial = True , context = {"request" : request })
83- serializer .is_valid (raise_exception = True )
84- instance = serializer .save ()
85- except (ValueError , TypeError ) as e :
86- # If find_existing_object fails (e.g., due to data issues), skip and try normal create
87- logger .debug (f"Could not find existing { change .object_type } : { e } " )
88- instance = None
109+ if _is_auto_created_component (change .object_type ):
110+ instance = _try_find_and_update_existing (data , change .object_type , serializer_class , request )
89111
90112 if not instance :
91- serializer = serializer_class (data = data , context = {"request" : request })
92- try :
93- serializer .is_valid (raise_exception = True )
94- instance = serializer .save ()
95- except ValidationError as e :
96- instance = find_existing_object (data , change .object_type )
97- if not instance :
98- raise e
99- created [change .ref_id ] = instance
113+ instance = _create_or_find_instance (data , change .object_type , serializer_class , request )
114+
115+ # Always add the instance to created dict so it can be referenced by subsequent changes
116+ if change .ref_id :
117+ created [change .ref_id ] = instance
100118
101119 elif change_type == ChangeType .UPDATE .value :
102120 if object_id := change .object_id :
0 commit comments