This commit is contained in:
parent
b55b50b12e
commit
35450a6cb8
|
|
@ -326,9 +326,20 @@ class Module(TrackingModelMixin, PrimaryModel, ConfigContextModel):
|
|||
|
||||
def save(self, *args, **kwargs):
|
||||
is_new = self.pk is None
|
||||
old_module_bay_id = None
|
||||
|
||||
if not is_new:
|
||||
old_module_bay_id = Module.objects.filter(pk=self.pk).values_list(
|
||||
'module_bay_id', flat=True
|
||||
).first()
|
||||
|
||||
super().save(*args, **kwargs)
|
||||
|
||||
if old_module_bay_id is not None and old_module_bay_id != self.module_bay_id:
|
||||
for child_bay in self.modulebays.select_related('module__module_bay'):
|
||||
child_bay.snapshot()
|
||||
child_bay.save()
|
||||
|
||||
adopt_components = getattr(self, '_adopt_components', False)
|
||||
disable_replication = getattr(self, '_disable_replication', False)
|
||||
|
||||
|
|
|
|||
|
|
@ -1188,6 +1188,103 @@ class ModuleBayTestCase(TestCase):
|
|||
self.assertEqual(movable_bay.parent_id, host_bay.pk)
|
||||
self.assertEqual(movable_bay.tree_id, host_bay.tree_id)
|
||||
|
||||
@tag('regression') # #22251
|
||||
def test_moving_module_reparents_child_module_bays(self):
|
||||
"""
|
||||
When a module is moved to a different module bay, each child ModuleBay
|
||||
(a bay that belongs to the module) must have its MPTT parent updated
|
||||
to the new host bay. Without the fix the children stay parented to the
|
||||
old bay even though Module.module_bay_id has changed.
|
||||
"""
|
||||
device_type = DeviceType.objects.first()
|
||||
device_role = DeviceRole.objects.first()
|
||||
site = Site.objects.first()
|
||||
device = Device.objects.create(
|
||||
name='Move Module Device',
|
||||
device_type=device_type,
|
||||
role=device_role,
|
||||
site=site,
|
||||
)
|
||||
bay_a = ModuleBay.objects.create(device=device, name='Bay A')
|
||||
bay_b = ModuleBay.objects.create(device=device, name='Bay B')
|
||||
|
||||
manufacturer = Manufacturer.objects.first()
|
||||
module_type = ModuleType.objects.create(
|
||||
manufacturer=manufacturer, model='Move Module Type'
|
||||
)
|
||||
module = Module.objects.create(
|
||||
device=device, module_bay=bay_a, module_type=module_type
|
||||
)
|
||||
|
||||
child_1 = ModuleBay.objects.create(device=device, module=module, name='Child Bay 1')
|
||||
child_2 = ModuleBay.objects.create(device=device, module=module, name='Child Bay 2')
|
||||
self.assertEqual(child_1.parent_id, bay_a.pk)
|
||||
self.assertEqual(child_2.parent_id, bay_a.pk)
|
||||
|
||||
# Move the module to bay_b.
|
||||
module.module_bay = bay_b
|
||||
module.save()
|
||||
|
||||
child_1.refresh_from_db()
|
||||
child_2.refresh_from_db()
|
||||
self.assertEqual(child_1.parent_id, bay_b.pk)
|
||||
self.assertEqual(child_2.parent_id, bay_b.pk)
|
||||
# Children must share the same MPTT tree as their new parent.
|
||||
bay_b.refresh_from_db()
|
||||
self.assertEqual(child_1.tree_id, bay_b.tree_id)
|
||||
self.assertEqual(child_2.tree_id, bay_b.tree_id)
|
||||
|
||||
@tag('regression') # #22251
|
||||
def test_moving_module_reparents_grandchild_module_bays(self):
|
||||
"""
|
||||
When a module is moved, grandchild ModuleBays (bays inside a module
|
||||
that is itself installed inside a child bay of the moved module) must
|
||||
also land in the new MPTT tree. MPTT moves subtrees atomically, so
|
||||
calling save() only on direct children is sufficient — this test
|
||||
documents and preserves that invariant for future tree-backend changes.
|
||||
"""
|
||||
device_type = DeviceType.objects.first()
|
||||
device_role = DeviceRole.objects.first()
|
||||
site = Site.objects.first()
|
||||
device = Device.objects.create(
|
||||
name='Grandchild Move Device',
|
||||
device_type=device_type,
|
||||
role=device_role,
|
||||
site=site,
|
||||
)
|
||||
bay_a = ModuleBay.objects.create(device=device, name='Bay A')
|
||||
bay_b = ModuleBay.objects.create(device=device, name='Bay B')
|
||||
|
||||
manufacturer = Manufacturer.objects.first()
|
||||
module_type = ModuleType.objects.create(
|
||||
manufacturer=manufacturer, model='Grandchild Move Type'
|
||||
)
|
||||
# Depth-1: module installed in bay_a, with one child bay.
|
||||
module_1 = Module.objects.create(device=device, module_bay=bay_a, module_type=module_type)
|
||||
child_bay = ModuleBay.objects.create(device=device, module=module_1, name='Child Bay')
|
||||
|
||||
# Depth-2: module installed in child_bay, with one grandchild bay.
|
||||
module_2 = Module.objects.create(device=device, module_bay=child_bay, module_type=module_type)
|
||||
grandchild_bay = ModuleBay.objects.create(device=device, module=module_2, name='Grandchild Bay')
|
||||
|
||||
self.assertEqual(child_bay.parent_id, bay_a.pk)
|
||||
self.assertEqual(grandchild_bay.parent_id, child_bay.pk)
|
||||
self.assertEqual(grandchild_bay.tree_id, bay_a.tree_id)
|
||||
|
||||
# Move the top-level module to bay_b.
|
||||
module_1.module_bay = bay_b
|
||||
module_1.save()
|
||||
|
||||
child_bay.refresh_from_db()
|
||||
grandchild_bay.refresh_from_db()
|
||||
bay_b.refresh_from_db()
|
||||
|
||||
self.assertEqual(child_bay.parent_id, bay_b.pk)
|
||||
self.assertEqual(child_bay.tree_id, bay_b.tree_id)
|
||||
# Grandchild's direct parent (child_bay) is unchanged; only tree placement moves.
|
||||
self.assertEqual(grandchild_bay.parent_id, child_bay.pk)
|
||||
self.assertEqual(grandchild_bay.tree_id, bay_b.tree_id)
|
||||
|
||||
def test_single_module_token(self):
|
||||
device_type = DeviceType.objects.first()
|
||||
device_role = DeviceRole.objects.first()
|
||||
|
|
|
|||
Loading…
Reference in New Issue