From 4075cc8518da585f35536f667c76c5293a520bdc Mon Sep 17 00:00:00 2001 From: jeremystretch Date: Tue, 28 Dec 2021 09:53:56 -0500 Subject: [PATCH] Restore front port component creation --- netbox/dcim/forms/models.py | 1 - netbox/dcim/forms/object_create.py | 93 +++++++++++++++++++++ netbox/dcim/views.py | 18 ++++ netbox/netbox/views/generic/object_views.py | 5 +- netbox/templates/generic/object_edit.html | 8 +- 5 files changed, 118 insertions(+), 7 deletions(-) diff --git a/netbox/dcim/forms/models.py b/netbox/dcim/forms/models.py index d5b52aed3..223f0a63f 100644 --- a/netbox/dcim/forms/models.py +++ b/netbox/dcim/forms/models.py @@ -1267,7 +1267,6 @@ class InterfaceForm(InterfaceCommonForm, CustomFieldModelForm): class FrontPortForm(CustomFieldModelForm): rear_port = DynamicModelChoiceField( queryset=RearPort.objects.all(), - required=False, query_params={ 'device_id': '$device', } diff --git a/netbox/dcim/forms/object_create.py b/netbox/dcim/forms/object_create.py index 8b8c00c6d..5e8daf38d 100644 --- a/netbox/dcim/forms/object_create.py +++ b/netbox/dcim/forms/object_create.py @@ -9,6 +9,8 @@ from utilities.forms import ( __all__ = ( 'ComponentCreateForm', + 'FrontPortCreateForm', + 'FrontPortTemplateCreateForm', 'VirtualChassisCreateForm', ) @@ -41,6 +43,97 @@ class ComponentCreateForm(BootstrapMixin, forms.Form): }, code='label_pattern_mismatch') +class FrontPortTemplateCreateForm(ComponentCreateForm): + rear_port_set = forms.MultipleChoiceField( + choices=[], + label='Rear ports', + help_text='Select one rear port assignment for each front port being created.', + ) + field_order = ( + 'name_pattern', 'label_pattern', 'rear_port_set', + ) + + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + + device_type = DeviceType.objects.get( + pk=self.initial.get('device_type') or self.data.get('device_type') + ) + + # 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_type.frontporttemplates.all() + ] + + # Populate rear port choices + choices = [] + rear_ports = RearPortTemplate.objects.filter(device_type=device_type) + 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_set'].choices = choices + + def get_iterative_data(self, iteration): + + # Assign rear port and position from selected set + rear_port, position = self.cleaned_data['rear_port_set'][iteration].split(':') + + return { + 'rear_port': int(rear_port), + 'rear_port_position': int(position), + } + + +class FrontPortCreateForm(ComponentCreateForm): + rear_port_set = forms.MultipleChoiceField( + choices=[], + label='Rear ports', + help_text='Select one rear port assignment for each front port being created.', + ) + field_order = ( + 'name_pattern', 'label_pattern', 'rear_port_set', + ) + + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + + device = Device.objects.get( + pk=self.initial.get('device') or self.data.get('device') + ) + + # 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_set'].choices = choices + + def get_iterative_data(self, iteration): + + # Assign rear port and position from selected set + rear_port, position = self.cleaned_data['rear_port_set'][iteration].split(':') + + return { + 'rear_port': int(rear_port), + 'rear_port_position': int(position), + } + + class VirtualChassisCreateForm(CustomFieldModelForm): region = DynamicModelChoiceField( queryset=Region.objects.all(), diff --git a/netbox/dcim/views.py b/netbox/dcim/views.py index 05416b720..bee7f9ef0 100644 --- a/netbox/dcim/views.py +++ b/netbox/dcim/views.py @@ -1219,8 +1219,17 @@ class InterfaceTemplateBulkDeleteView(generic.BulkDeleteView): class FrontPortTemplateCreateView(generic.ComponentCreateView): queryset = FrontPortTemplate.objects.all() + form = forms.FrontPortTemplateCreateForm model_form = forms.FrontPortTemplateForm + def initialize_forms(self, request): + form, model_form = super().initialize_forms(request) + + model_form.fields.pop('rear_port') + model_form.fields.pop('rear_port_position') + + return form, model_form + class FrontPortTemplateEditView(generic.ObjectEditView): queryset = FrontPortTemplate.objects.all() @@ -2085,8 +2094,17 @@ class FrontPortView(generic.ObjectView): class FrontPortCreateView(generic.ComponentCreateView): queryset = FrontPort.objects.all() + form = forms.FrontPortCreateForm model_form = forms.FrontPortForm + def initialize_forms(self, request): + form, model_form = super().initialize_forms(request) + + model_form.fields.pop('rear_port') + model_form.fields.pop('rear_port_position') + + return form, model_form + class FrontPortEditView(generic.ObjectEditView): queryset = FrontPort.objects.all() diff --git a/netbox/netbox/views/generic/object_views.py b/netbox/netbox/views/generic/object_views.py index 682501895..a44d027b2 100644 --- a/netbox/netbox/views/generic/object_views.py +++ b/netbox/netbox/views/generic/object_views.py @@ -735,7 +735,6 @@ class ComponentCreateView(GetReturnURLMixin, ObjectPermissionRequiredMixin, View Validate form values and set errors on the form object as they are detected. If no errors are found, signal success messages. """ - logger = logging.getLogger('netbox.views.ComponentCreateView') if form.is_valid(): new_components = [] @@ -749,8 +748,8 @@ class ComponentCreateView(GetReturnURLMixin, ObjectPermissionRequiredMixin, View data['name'] = name data['label'] = label - # if hasattr(form, 'get_iterative_data'): - # data.update(form.get_iterative_data(i)) + if hasattr(form, 'get_iterative_data'): + data.update(form.get_iterative_data(i)) component_form = self.model_form(data) diff --git a/netbox/templates/generic/object_edit.html b/netbox/templates/generic/object_edit.html index d72210dc7..c94981305 100644 --- a/netbox/templates/generic/object_edit.html +++ b/netbox/templates/generic/object_edit.html @@ -31,13 +31,15 @@
{% csrf_token %} - {% for field in form.hidden_fields %} - {{ field }} - {% endfor %} {% block form %} {% if form.Meta.fieldsets %} + {# Render hidden fields #} + {% for field in form.hidden_fields %} + {{ field }} + {% endfor %} + {# Render grouped fields according to Form #} {% for group, fields in form.Meta.fieldsets %}