Skip to content

Commit 4096612

Browse files
committed
feat: refactor component creation logic to prevent duplicates and improve readability
Signed-off-by: Michal Fiedorowicz <mfiedorowicz@netboxlabs.com>
1 parent 7393460 commit 4096612

File tree

1 file changed

+51
-33
lines changed

1 file changed

+51
-33
lines changed

netbox_diode_plugin/api/applier.py

Lines changed: 51 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -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+
57100
def _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

Comments
 (0)