diff --git a/netbox/dcim/forms/bulk_import.py b/netbox/dcim/forms/bulk_import.py index 1ea789068..2d0d8490e 100644 --- a/netbox/dcim/forms/bulk_import.py +++ b/netbox/dcim/forms/bulk_import.py @@ -1335,6 +1335,13 @@ class MACAddressImportForm(NetBoxModelImportForm): class CableImportForm(NetBoxModelImportForm): # Termination A + side_a_site = CSVModelChoiceField( + label=_('Side A site'), + queryset=Site.objects.all(), + required=False, + to_field_name='name', + help_text=_('Site of parent device A (if any)'), + ) side_a_device = CSVModelChoiceField( label=_('Side A device'), queryset=Device.objects.all(), @@ -1353,6 +1360,13 @@ class CableImportForm(NetBoxModelImportForm): ) # Termination B + side_b_site = CSVModelChoiceField( + label=_('Side B site'), + queryset=Site.objects.all(), + required=False, + to_field_name='name', + help_text=_('Site of parent device B (if any)'), + ) side_b_device = CSVModelChoiceField( label=_('Side B device'), queryset=Device.objects.all(), @@ -1400,10 +1414,29 @@ class CableImportForm(NetBoxModelImportForm): class Meta: model = Cable fields = [ - 'side_a_device', 'side_a_type', 'side_a_name', 'side_b_device', 'side_b_type', 'side_b_name', 'type', - 'status', 'tenant', 'label', 'color', 'length', 'length_unit', 'description', 'comments', 'tags', + 'side_a_site', 'side_a_device', 'side_a_type', 'side_a_name', 'side_b_site', 'side_b_device', 'side_b_type', + 'side_b_name', 'type', 'status', 'tenant', 'label', 'color', 'length', 'length_unit', 'description', + 'comments', 'tags', ] + def __init__(self, data=None, *args, **kwargs): + super().__init__(data, *args, **kwargs) + + if data: + # Limit choices for side_a_device to the assigned side_a_site + if side_a_site := data.get('side_a_site'): + side_a_device_params = {f'site__{self.fields["side_a_site"].to_field_name}': side_a_site} + self.fields['side_a_device'].queryset = self.fields['side_a_device'].queryset.filter( + **side_a_device_params + ) + + # Limit choices for side_b_device to the assigned side_b_site + if side_b_site := data.get('side_b_site'): + side_b_device_params = {f'site__{self.fields["side_b_site"].to_field_name}': side_b_site} + self.fields['side_b_device'].queryset = self.fields['side_b_device'].queryset.filter( + **side_b_device_params + ) + def _clean_side(self, side): """ Derive a Cable's A/B termination objects. diff --git a/netbox/dcim/tests/test_views.py b/netbox/dcim/tests/test_views.py index 7eda9ef4d..8755930b2 100644 --- a/netbox/dcim/tests/test_views.py +++ b/netbox/dcim/tests/test_views.py @@ -3266,17 +3266,21 @@ class CableTestCase( @classmethod def setUpTestData(cls): - site = Site.objects.create(name='Site 1', slug='site-1') + sites = ( + Site(name='Site 1', slug='site-1'), + Site(name='Site 2', slug='site-2'), + ) + Site.objects.bulk_create(sites) manufacturer = Manufacturer.objects.create(name='Manufacturer 1', slug='manufacturer-1') devicetype = DeviceType.objects.create(model='Device Type 1', manufacturer=manufacturer) role = DeviceRole.objects.create(name='Device Role 1', slug='device-role-1') vc = VirtualChassis.objects.create(name='Virtual Chassis') devices = ( - Device(name='Device 1', site=site, device_type=devicetype, role=role), - Device(name='Device 2', site=site, device_type=devicetype, role=role), - Device(name='Device 3', site=site, device_type=devicetype, role=role), - Device(name='Device 4', site=site, device_type=devicetype, role=role), + Device(name='Device 1', site=sites[0], device_type=devicetype, role=role), + Device(name='Device 2', site=sites[0], device_type=devicetype, role=role), + Device(name='Device 3', site=sites[0], device_type=devicetype, role=role), + Device(name='Device 1', site=sites[1], device_type=devicetype, role=role), ) Device.objects.bulk_create(devices) @@ -3328,12 +3332,12 @@ class CableTestCase( } cls.csv_data = ( - "side_a_device,side_a_type,side_a_name,side_b_device,side_b_type,side_b_name", - "Device 3,dcim.interface,Interface 1,Device 4,dcim.interface,Interface 1", - "Device 3,dcim.interface,Interface 2,Device 4,dcim.interface,Interface 2", - "Device 3,dcim.interface,Interface 3,Device 4,dcim.interface,Interface 3", - "Device 1,dcim.interface,Device 2 Interface,Device 4,dcim.interface,Interface 4", - "Device 1,dcim.interface,Device 3 Interface,Device 4,dcim.interface,Interface 5", + "side_a_site,side_a_device,side_a_type,side_a_name,side_b_site,side_b_device,side_b_type,side_b_name", + "Site 1,Device 3,dcim.interface,Interface 1,Site 2,Device 1,dcim.interface,Interface 1", + "Site 1,Device 3,dcim.interface,Interface 2,Site 2,Device 1,dcim.interface,Interface 2", + "Site 1,Device 3,dcim.interface,Interface 3,Site 2,Device 1,dcim.interface,Interface 3", + "Site 1,Device 1,dcim.interface,Device 2 Interface,Site 2,Device 1,dcim.interface,Interface 4", + "Site 1,Device 1,dcim.interface,Device 3 Interface,Site 2,Device 1,dcim.interface,Interface 5", ) cls.csv_update_data = (