From 4790dbba96d096fa92b8251a37e4c8ed27388756 Mon Sep 17 00:00:00 2001 From: Jeremy Stretch Date: Tue, 18 Nov 2025 10:56:05 -0500 Subject: [PATCH] Exclude occupied rear port & position pairs from list of choices --- netbox/dcim/forms/model_forms.py | 31 +++++++++++++++++++++++-------- 1 file changed, 23 insertions(+), 8 deletions(-) diff --git a/netbox/dcim/forms/model_forms.py b/netbox/dcim/forms/model_forms.py index 028315211..195f922ea 100644 --- a/netbox/dcim/forms/model_forms.py +++ b/netbox/dcim/forms/model_forms.py @@ -1601,19 +1601,13 @@ class FrontPortForm(ModularDeviceComponentForm): def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) - if device_id := (self.data.get('device') or self.initial.get('device')): + if device_id := self.data.get('device') or self.initial.get('device'): device = Device.objects.get(pk=device_id) else: return # Populate rear port choices - choices = [] - for rear_port in RearPort.objects.filter(device=device): - for i in range(1, rear_port.positions + 1): - choices.append( - ('{}:{}'.format(rear_port.pk, i), '{}:{}'.format(rear_port.name, i)) - ) - self.fields['rear_ports'].choices = choices + self.fields['rear_ports'].choices = self._get_rear_port_choices(device, self.instance) # Set initial rear port assignments if self.instance.pk: @@ -1651,6 +1645,27 @@ class FrontPortForm(ModularDeviceComponentForm): ) PortAssignment.objects.bulk_create(assignments) + def _get_rear_port_choices(self, device, front_port): + """ + Return a list of choices representing each available rear port & position pair on the device, excluding those + assigned to the specified instance. + """ + occupied_rear_port_positions = [ + f'{assignment.rear_port_id}:{assignment.rear_port_position}' + for assignment in PortAssignment.objects.filter(front_port__device=device).exclude(front_port=front_port) + ] + + choices = [] + for rear_port in RearPort.objects.filter(device=device): + for i in range(1, rear_port.positions + 1): + pair_id = f'{rear_port.pk}:{i}' + if pair_id not in occupied_rear_port_positions: + pair_label = f'{rear_port.name}:{i}' + choices.append( + (pair_id, pair_label) + ) + return choices + class RearPortForm(ModularDeviceComponentForm): fieldsets = (