Browse Source

Fixes #21127: Clear _path on interfaces when removed from cable

When editing a cable to remove an interface from the B side, the _path
field on the removed interface was not being cleared. This caused the
interface table to display stale connection info via _path.destinations.

Two changes:
- Signal handler now clears _path when termination removed from origins
- CablePath.delete() clears _path on origins (mirrors save() behavior)
Jason Novinger 1 day ago
parent
commit
f113557e81
3 changed files with 16 additions and 1 deletions
  1. 10 0
      netbox/dcim/models/cables.py
  2. 2 0
      netbox/dcim/signals.py
  3. 4 1
      netbox/dcim/tests/test_cablepaths.py

+ 10 - 0
netbox/dcim/models/cables.py

@@ -657,6 +657,16 @@ class CablePath(models.Model):
         origin_ids = [decompile_path_node(node)[1] for node in self.path[0]]
         origin_model.objects.filter(pk__in=origin_ids).update(_path=self.pk)
 
+    def delete(self, *args, **kwargs):
+        # Mirror save() - clear _path on origins to prevent stale references
+        # in table views that render _path.destinations
+        if self.path:
+            origin_model = self.origin_type.model_class()
+            origin_ids = [decompile_path_node(node)[1] for node in self.path[0]]
+            origin_model.objects.filter(pk__in=origin_ids, _path=self.pk).update(_path=None)
+
+        super().delete(*args, **kwargs)
+
     @property
     def origin_type(self):
         if self.path:

+ 2 - 0
netbox/dcim/signals.py

@@ -170,6 +170,8 @@ def nullify_connected_endpoints(instance, **kwargs):
         # Remove the deleted CableTermination if it's one of the path's originating nodes
         if instance.termination in cablepath.origins:
             cablepath.origins.remove(instance.termination)
+            # Clear _path on the removed origin to prevent stale connection display
+            model.objects.filter(pk=instance.termination_id, _path=cablepath.pk).update(_path=None)
         cablepath.retrace()
 
 

+ 4 - 1
netbox/dcim/tests/test_cablepaths.py

@@ -2806,7 +2806,6 @@ class LegacyCablePathTests(CablePathTestCase):
         interface2 = Interface.objects.create(device=self.device, name='Interface 2')
         interface3 = Interface.objects.create(device=self.device, name='Interface 3')
 
-        # Create cables 1
         cable1 = Cable(
             a_terminations=[interface1],
             b_terminations=[interface2, interface3]
@@ -2838,6 +2837,10 @@ class LegacyCablePathTests(CablePathTestCase):
             is_active=True
         )
 
+        # Verify _path is cleared on removed interface (#21127)
+        interface3.refresh_from_db()
+        self.assertPathIsNotSet(interface3)
+
     def test_401_exclude_midspan_devices(self):
         """
         [IF1] --C1-- [FP1][Test Device][RP1] --C2-- [RP2][Test Device][FP2] --C3-- [IF2]