Fix FrontPortCreateForm

This commit is contained in:
Jeremy Stretch
2025-11-18 11:21:29 -05:00
parent 4790dbba96
commit a7c3971a43
3 changed files with 17 additions and 47 deletions

View File

@@ -1617,6 +1617,7 @@ class FrontPortForm(ModularDeviceComponentForm):
] ]
def clean(self): def clean(self):
super().clean()
# Count of selected rear port & position pairs much match the assigned number of positions # Count of selected rear port & position pairs much match the assigned number of positions
if len(self.cleaned_data['rear_ports']) != self.cleaned_data['positions']: if len(self.cleaned_data['rear_ports']) != self.cleaned_data['positions']:
@@ -1652,7 +1653,7 @@ class FrontPortForm(ModularDeviceComponentForm):
""" """
occupied_rear_port_positions = [ occupied_rear_port_positions = [
f'{assignment.rear_port_id}:{assignment.rear_port_position}' f'{assignment.rear_port_id}:{assignment.rear_port_position}'
for assignment in PortAssignment.objects.filter(front_port__device=device).exclude(front_port=front_port) for assignment in PortAssignment.objects.filter(front_port__device=device).exclude(front_port=front_port.pk)
] ]
choices = [] choices = []

View File

@@ -269,58 +269,30 @@ class FrontPortCreateForm(ComponentCreateForm, model_forms.FrontPortForm):
} }
) )
) )
rear_port = forms.MultipleChoiceField(
choices=[],
label=_('Rear ports'),
help_text=_('Select one rear port assignment for each front port being created.'),
widget=forms.SelectMultiple(attrs={'size': 6})
)
# Override fieldsets from FrontPortForm to omit rear_port_position # Override fieldsets from FrontPortForm to omit rear_port_position
fieldsets = ( fieldsets = (
FieldSet( FieldSet(
'device', 'module', 'name', 'label', 'type', 'color', 'rear_port', 'mark_connected', 'description', 'tags', 'device', 'module', 'name', 'label', 'type', 'color', 'positions', 'rear_ports', 'mark_connected',
'description', 'tags',
), ),
) )
class Meta(model_forms.FrontPortForm.Meta): class Meta:
exclude = ('name', 'label', 'positions') model = FrontPort
fields = [
def __init__(self, *args, **kwargs): 'device', 'module', 'type', 'color', 'positions', 'mark_connected', 'description', 'owner', 'tags',
super().__init__(*args, **kwargs)
if device_id := self.data.get('device') or self.initial.get('device'):
device = Device.objects.get(pk=device_id)
else:
return
# Determine which rear port positions are occupied. These will be excluded from the list of available
# mappings.
occupied_port_positions = [
(front_port.rear_port_id, front_port.rear_port_position)
for front_port in device.frontports.all()
] ]
# Populate rear port choices
choices = []
rear_ports = RearPort.objects.filter(device=device)
for rear_port in rear_ports:
for i in range(1, rear_port.positions + 1):
if (rear_port.pk, i) not in occupied_port_positions:
choices.append(
('{}:{}'.format(rear_port.pk, i), '{}:{}'.format(rear_port.name, i))
)
self.fields['rear_port'].choices = choices
def clean(self): def clean(self):
super().clean() super(NetBoxModelForm, self).clean()
# Check that the number of FrontPorts to be created matches the selected number of RearPort positions # Check that the number of FrontPorts to be created matches the selected number of RearPort positions
frontport_count = len(self.cleaned_data['name']) frontport_count = len(self.cleaned_data['name'])
rearport_count = len(self.cleaned_data['rear_port']) rearport_count = len(self.cleaned_data['rear_ports'])
if frontport_count != rearport_count: if frontport_count != rearport_count:
raise forms.ValidationError({ raise forms.ValidationError({
'rear_port': _( 'rear_ports': _(
"The number of front ports to be created ({frontport_count}) must match the selected number of " "The number of front ports to be created ({frontport_count}) must match the selected number of "
"rear port positions ({rearport_count})." "rear port positions ({rearport_count})."
).format( ).format(
@@ -330,13 +302,10 @@ class FrontPortCreateForm(ComponentCreateForm, model_forms.FrontPortForm):
}) })
def get_iterative_data(self, iteration): def get_iterative_data(self, iteration):
positions = self.cleaned_data['positions']
# Assign rear port and position from selected set offset = positions * iteration
rear_port, position = self.cleaned_data['rear_port'][iteration].split(':')
return { return {
'rear_port': int(rear_port), 'rear_ports': self.cleaned_data['rear_ports'][offset:offset + positions]
'rear_port_position': int(position),
} }

View File

@@ -1,6 +1,5 @@
import logging import logging
from collections import defaultdict from collections import defaultdict
from copy import deepcopy
from django.contrib import messages from django.contrib import messages
from django.db import router, transaction from django.db import router, transaction
@@ -563,7 +562,7 @@ class ComponentCreateView(GetReturnURLMixin, BaseObjectView):
if form.is_valid(): if form.is_valid():
new_components = [] new_components = []
data = deepcopy(request.POST) data = request.POST.copy()
pattern_count = len(form.cleaned_data[self.form.replication_fields[0]]) pattern_count = len(form.cleaned_data[self.form.replication_fields[0]])
for i in range(pattern_count): for i in range(pattern_count):
@@ -572,7 +571,8 @@ class ComponentCreateView(GetReturnURLMixin, BaseObjectView):
data[field_name] = form.cleaned_data[field_name][i] data[field_name] = form.cleaned_data[field_name][i]
if hasattr(form, 'get_iterative_data'): if hasattr(form, 'get_iterative_data'):
data.update(form.get_iterative_data(i)) for k, v in form.get_iterative_data(i).items():
data.setlist(k, v)
component_form = self.model_form(data) component_form = self.model_form(data)