mirror of
https://github.com/netbox-community/netbox.git
synced 2026-01-16 16:52:17 -06:00
Fixes #21124: Fix rear port selection when creating front ports on a module type
This commit is contained in:
@@ -140,9 +140,6 @@ class FrontPortFormMixin(forms.Form):
|
|||||||
widget=forms.SelectMultiple(attrs={'size': 8})
|
widget=forms.SelectMultiple(attrs={'size': 8})
|
||||||
)
|
)
|
||||||
|
|
||||||
port_mapping_model = PortMapping
|
|
||||||
parent_field = 'device'
|
|
||||||
|
|
||||||
def clean(self):
|
def clean(self):
|
||||||
super().clean()
|
super().clean()
|
||||||
|
|
||||||
@@ -203,3 +200,22 @@ class FrontPortFormMixin(forms.Form):
|
|||||||
using=connection,
|
using=connection,
|
||||||
update_fields=None
|
update_fields=None
|
||||||
)
|
)
|
||||||
|
|
||||||
|
def _get_rear_port_choices(self, parent_filter, front_port):
|
||||||
|
"""
|
||||||
|
Return a list of choices representing each available rear port & position pair on the parent object (identified
|
||||||
|
by a Q filter), excluding those assigned to the specified instance.
|
||||||
|
"""
|
||||||
|
occupied_rear_port_positions = [
|
||||||
|
f'{mapping.rear_port_id}:{mapping.rear_port_position}'
|
||||||
|
for mapping in self.port_mapping_model.objects.filter(parent_filter).exclude(front_port=front_port.pk)
|
||||||
|
]
|
||||||
|
|
||||||
|
choices = []
|
||||||
|
for rear_port in self.rear_port_model.objects.filter(parent_filter):
|
||||||
|
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
|
||||||
|
|||||||
@@ -1124,9 +1124,8 @@ class FrontPortTemplateForm(FrontPortFormMixin, ModularComponentTemplateForm):
|
|||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
# Override FrontPortFormMixin attrs
|
|
||||||
port_mapping_model = PortTemplateMapping
|
port_mapping_model = PortTemplateMapping
|
||||||
parent_field = 'device_type'
|
rear_port_model = RearPortTemplate
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = FrontPortTemplate
|
model = FrontPortTemplate
|
||||||
@@ -1137,13 +1136,14 @@ class FrontPortTemplateForm(FrontPortFormMixin, ModularComponentTemplateForm):
|
|||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
super().__init__(*args, **kwargs)
|
super().__init__(*args, **kwargs)
|
||||||
|
|
||||||
|
# Populate rear port choices based on parent DeviceType or ModuleType
|
||||||
if device_type_id := self.data.get('device_type') or self.initial.get('device_type'):
|
if device_type_id := self.data.get('device_type') or self.initial.get('device_type'):
|
||||||
device_type = DeviceType.objects.get(pk=device_type_id)
|
parent_filter = Q(device_type=device_type_id)
|
||||||
|
elif module_type_id := self.data.get('module_type') or self.initial.get('module_type'):
|
||||||
|
parent_filter = Q(module_type=module_type_id)
|
||||||
else:
|
else:
|
||||||
return
|
return
|
||||||
|
self.fields['rear_ports'].choices = self._get_rear_port_choices(parent_filter, self.instance)
|
||||||
# Populate rear port choices
|
|
||||||
self.fields['rear_ports'].choices = self._get_rear_port_choices(device_type, self.instance)
|
|
||||||
|
|
||||||
# Set initial rear port mappings
|
# Set initial rear port mappings
|
||||||
if self.instance.pk:
|
if self.instance.pk:
|
||||||
@@ -1152,27 +1152,6 @@ class FrontPortTemplateForm(FrontPortFormMixin, ModularComponentTemplateForm):
|
|||||||
for mapping in PortTemplateMapping.objects.filter(front_port_id=self.instance.pk)
|
for mapping in PortTemplateMapping.objects.filter(front_port_id=self.instance.pk)
|
||||||
]
|
]
|
||||||
|
|
||||||
def _get_rear_port_choices(self, device_type, front_port):
|
|
||||||
"""
|
|
||||||
Return a list of choices representing each available rear port & position pair on the device type, excluding
|
|
||||||
those assigned to the specified instance.
|
|
||||||
"""
|
|
||||||
occupied_rear_port_positions = [
|
|
||||||
f'{mapping.rear_port_id}:{mapping.rear_port_position}'
|
|
||||||
for mapping in device_type.port_mappings.exclude(front_port=front_port.pk)
|
|
||||||
]
|
|
||||||
|
|
||||||
choices = []
|
|
||||||
for rear_port in RearPortTemplate.objects.filter(device_type=device_type):
|
|
||||||
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 RearPortTemplateForm(ModularComponentTemplateForm):
|
class RearPortTemplateForm(ModularComponentTemplateForm):
|
||||||
fieldsets = (
|
fieldsets = (
|
||||||
@@ -1619,6 +1598,9 @@ class FrontPortForm(FrontPortFormMixin, ModularDeviceComponentForm):
|
|||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
port_mapping_model = PortMapping
|
||||||
|
rear_port_model = RearPort
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = FrontPort
|
model = FrontPort
|
||||||
fields = [
|
fields = [
|
||||||
@@ -1629,13 +1611,12 @@ class FrontPortForm(FrontPortFormMixin, ModularDeviceComponentForm):
|
|||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
super().__init__(*args, **kwargs)
|
super().__init__(*args, **kwargs)
|
||||||
|
|
||||||
|
# Populate rear port choices
|
||||||
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)
|
parent_filter = Q(device=device_id)
|
||||||
else:
|
else:
|
||||||
return
|
return
|
||||||
|
self.fields['rear_ports'].choices = self._get_rear_port_choices(parent_filter, self.instance)
|
||||||
# Populate rear port choices
|
|
||||||
self.fields['rear_ports'].choices = self._get_rear_port_choices(device, self.instance)
|
|
||||||
|
|
||||||
# Set initial rear port mappings
|
# Set initial rear port mappings
|
||||||
if self.instance.pk:
|
if self.instance.pk:
|
||||||
@@ -1644,27 +1625,6 @@ class FrontPortForm(FrontPortFormMixin, ModularDeviceComponentForm):
|
|||||||
for mapping in PortMapping.objects.filter(front_port_id=self.instance.pk)
|
for mapping in PortMapping.objects.filter(front_port_id=self.instance.pk)
|
||||||
]
|
]
|
||||||
|
|
||||||
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'{mapping.rear_port_id}:{mapping.rear_port_position}'
|
|
||||||
for mapping in device.port_mappings.exclude(front_port=front_port.pk)
|
|
||||||
]
|
|
||||||
|
|
||||||
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):
|
class RearPortForm(ModularDeviceComponentForm):
|
||||||
fieldsets = (
|
fieldsets = (
|
||||||
|
|||||||
Reference in New Issue
Block a user