diff --git a/netbox/dcim/models/device_components.py b/netbox/dcim/models/device_components.py index 9c44e0494..dc8c1e455 100644 --- a/netbox/dcim/models/device_components.py +++ b/netbox/dcim/models/device_components.py @@ -1222,6 +1222,8 @@ class ModuleBay(ModularComponentModel, TrackingModelMixin, MPTTModel): def save(self, *args, **kwargs): if self.module: self.parent = self.module.module_bay + else: + self.parent = None super().save(*args, **kwargs) diff --git a/netbox/dcim/tests/test_models.py b/netbox/dcim/tests/test_models.py index e67f32e60..f41509808 100644 --- a/netbox/dcim/tests/test_models.py +++ b/netbox/dcim/tests/test_models.py @@ -841,6 +841,32 @@ class ModuleBayTestCase(TestCase): nested_bay = module.modulebays.get(name='SFP A-21') self.assertEqual(nested_bay.label, 'A-21') + @tag('regression') # #20912 + def test_module_bay_parent_cleared_when_module_removed(self): + """Test that the parent field is properly cleared when a module bay's module assignment is removed""" + device = Device.objects.first() + manufacturer = Manufacturer.objects.first() + module_type = ModuleType.objects.create(manufacturer=manufacturer, model='Test Module Type') + bay1 = ModuleBay.objects.create(device=device, name='Test Bay 1') + bay2 = ModuleBay.objects.create(device=device, name='Test Bay 2') + + # Install a module in bay1 + module1 = Module.objects.create(device=device, module_bay=bay1, module_type=module_type) + + # Assign bay2 to module1 and verify parent is now set to bay1 (module1's bay) + bay2.module = module1 + bay2.save() + bay2.refresh_from_db() + self.assertEqual(bay2.parent, bay1) + self.assertEqual(bay2.module, module1) + + # Clear the module assignment (return bay2 to device level) Verify parent is cleared + bay2.module = None + bay2.save() + bay2.refresh_from_db() + self.assertIsNone(bay2.parent) + self.assertIsNone(bay2.module) + class CableTestCase(TestCase):