Arthur 1 месяц назад
Родитель
Сommit
be86c50204

+ 2 - 2
netbox/dcim/forms/bulk_edit.py

@@ -1757,8 +1757,8 @@ class VirtualDeviceContextBulkEditForm(PrimaryModelBulkEditForm):
     def __init__(self, *args, **kwargs):
         super().__init__(*args, **kwargs)
 
-        # Remove parent device passed as context to avoid conflicts with the actual device field
-        # on this form (see bug #21990)
+        # The ?device=<id> GET param is navigation context (filter), not an intent to change the
+        # device field — drop it from initial so Django's changed_data doesn't treat it as an edit.
         self.initial.pop('device', None)
 
 

+ 27 - 0
netbox/dcim/tests/test_views.py

@@ -4027,6 +4027,33 @@ class VirtualDeviceContextTestCase(ViewTestCases.PrimaryObjectViewTestCase):
             'status': VirtualDeviceContextStatusChoices.STATUS_OFFLINE,
         }
 
+    def test_bulk_edit_device_context_preserves_device(self):
+        """
+        Regression test: Bulk editing VDCs from the Device's VDCs tab (URL contains
+        ?device=<id>) must not clear the device field on those VDCs.
+        """
+        self.add_permissions('dcim.view_virtualdevicecontext', 'dcim.change_virtualdevicecontext')
+
+        device = VirtualDeviceContext.objects.filter(device__isnull=False).first().device
+        vdcs = list(VirtualDeviceContext.objects.filter(device=device)[:3])
+        pk_list = [vdc.pk for vdc in vdcs]
+
+        data = {
+            'pk': pk_list,
+            '_apply': True,
+            # Only change status — device is intentionally omitted
+            'status': VirtualDeviceContextStatusChoices.STATUS_PLANNED,
+        }
+
+        # Simulate navigation from Device -> VDCs tab by passing ?device=<id> as GET param
+        url = reverse('dcim:virtualdevicecontext_bulk_edit') + f'?device={device.pk}'
+        response = self.client.post(url, data)
+        self.assertHttpStatus(response, 302)
+
+        for vdc in VirtualDeviceContext.objects.filter(pk__in=pk_list):
+            self.assertEqual(vdc.device, device, msg=f"Device was unexpectedly cleared on VDC '{vdc.name}'")
+            self.assertEqual(vdc.status, VirtualDeviceContextStatusChoices.STATUS_PLANNED)
+
 
 class MACAddressTestCase(ViewTestCases.PrimaryObjectViewTestCase):
     model = MACAddress

+ 2 - 2
netbox/virtualization/forms/bulk_edit.py

@@ -164,8 +164,8 @@ class VirtualMachineBulkEditForm(PrimaryModelBulkEditForm):
     def __init__(self, *args, **kwargs):
         super().__init__(*args, **kwargs)
 
-        # Remove parent device passed as context to avoid conflicts with the actual device field
-        # on this form (see bug #21990)
+        # The ?device=<id> GET param is navigation context (filter), not an intent to change the
+        # device field — drop it from initial so Django's changed_data doesn't treat it as an edit.
         self.initial.pop('device', None)
 
         # Set unit labels based on configured RAM_BASE_UNIT / DISK_BASE_UNIT (MB vs MiB)