diff --git a/netbox/dcim/forms/model_forms.py b/netbox/dcim/forms/model_forms.py index 195f922ea..67cd58ad1 100644 --- a/netbox/dcim/forms/model_forms.py +++ b/netbox/dcim/forms/model_forms.py @@ -1617,6 +1617,7 @@ class FrontPortForm(ModularDeviceComponentForm): ] def clean(self): + super().clean() # 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']: @@ -1652,7 +1653,7 @@ class FrontPortForm(ModularDeviceComponentForm): """ 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) + for assignment in PortAssignment.objects.filter(front_port__device=device).exclude(front_port=front_port.pk) ] choices = [] diff --git a/netbox/dcim/forms/object_create.py b/netbox/dcim/forms/object_create.py index aa14ed9cc..028ab6c3c 100644 --- a/netbox/dcim/forms/object_create.py +++ b/netbox/dcim/forms/object_create.py @@ -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 fieldsets = ( 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): - exclude = ('name', 'label', 'positions') - - def __init__(self, *args, **kwargs): - 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() + class Meta: + model = FrontPort + fields = [ + 'device', 'module', 'type', 'color', 'positions', 'mark_connected', 'description', 'owner', 'tags', ] - # 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): - super().clean() + super(NetBoxModelForm, self).clean() # Check that the number of FrontPorts to be created matches the selected number of RearPort positions 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: raise forms.ValidationError({ - 'rear_port': _( + 'rear_ports': _( "The number of front ports to be created ({frontport_count}) must match the selected number of " "rear port positions ({rearport_count})." ).format( @@ -330,13 +302,10 @@ class FrontPortCreateForm(ComponentCreateForm, model_forms.FrontPortForm): }) def get_iterative_data(self, iteration): - - # Assign rear port and position from selected set - rear_port, position = self.cleaned_data['rear_port'][iteration].split(':') - + positions = self.cleaned_data['positions'] + offset = positions * iteration return { - 'rear_port': int(rear_port), - 'rear_port_position': int(position), + 'rear_ports': self.cleaned_data['rear_ports'][offset:offset + positions] } diff --git a/netbox/netbox/views/generic/object_views.py b/netbox/netbox/views/generic/object_views.py index 88a3456f7..818cea610 100644 --- a/netbox/netbox/views/generic/object_views.py +++ b/netbox/netbox/views/generic/object_views.py @@ -1,6 +1,5 @@ import logging from collections import defaultdict -from copy import deepcopy from django.contrib import messages from django.db import router, transaction @@ -563,7 +562,7 @@ class ComponentCreateView(GetReturnURLMixin, BaseObjectView): if form.is_valid(): new_components = [] - data = deepcopy(request.POST) + data = request.POST.copy() pattern_count = len(form.cleaned_data[self.form.replication_fields[0]]) for i in range(pattern_count): @@ -572,7 +571,8 @@ class ComponentCreateView(GetReturnURLMixin, BaseObjectView): data[field_name] = form.cleaned_data[field_name][i] 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)