From a2fe23549b9472c6229268bc97300766810ace7f Mon Sep 17 00:00:00 2001 From: jeremystretch Date: Wed, 23 Feb 2022 13:02:14 -0500 Subject: [PATCH] Closes #8667: Support position patterning when creating module bays & templates --- netbox/dcim/forms/object_create.py | 41 +++++++++++++++---- netbox/dcim/tables/devices.py | 4 +- netbox/dcim/views.py | 8 ++-- netbox/netbox/views/generic/object_views.py | 12 +++--- .../dcim/modulebaytemplate_create.html | 7 ++++ 5 files changed, 51 insertions(+), 21 deletions(-) create mode 100644 netbox/templates/dcim/modulebaytemplate_create.html diff --git a/netbox/dcim/forms/object_create.py b/netbox/dcim/forms/object_create.py index 0a1a34d1b..a13548d81 100644 --- a/netbox/dcim/forms/object_create.py +++ b/netbox/dcim/forms/object_create.py @@ -8,11 +8,13 @@ from utilities.forms import ( ) __all__ = ( - 'ModularComponentTemplateCreateForm', - 'DeviceComponentCreateForm', 'ComponentTemplateCreateForm', + 'DeviceComponentCreateForm', 'FrontPortCreateForm', 'FrontPortTemplateCreateForm', + 'ModularComponentTemplateCreateForm', + 'ModuleBayCreateForm', + 'ModuleBayTemplateCreateForm', 'VirtualChassisCreateForm', ) @@ -34,14 +36,17 @@ class ComponentCreateForm(BootstrapMixin, forms.Form): def clean(self): super().clean() - # Validate that the number of components being created from both the name_pattern and label_pattern are equal - if self.cleaned_data['label_pattern']: - name_pattern_count = len(self.cleaned_data['name_pattern']) - label_pattern_count = len(self.cleaned_data['label_pattern']) - if name_pattern_count != label_pattern_count: + # Validate that all patterned fields generate an equal number of values + patterned_fields = [ + field_name for field_name in self.fields if field_name.endswith('_pattern') + ] + pattern_count = len(self.cleaned_data['name_pattern']) + for field_name in patterned_fields: + value_count = len(self.cleaned_data[field_name]) + if self.cleaned_data[field_name] and value_count != pattern_count: raise forms.ValidationError({ - 'label_pattern': f'The provided name pattern will create {name_pattern_count} components, however ' - f'{label_pattern_count} labels will be generated. These counts must match.' + field_name: f'The provided pattern specifies {value_count} values, but {pattern_count} are ' + f'expected.' }, code='label_pattern_mismatch') @@ -176,6 +181,24 @@ class FrontPortCreateForm(DeviceComponentCreateForm): } +class ModuleBayTemplateCreateForm(ComponentTemplateCreateForm): + position_pattern = ExpandableNameField( + label='Position', + required=False, + help_text='Alphanumeric ranges are supported. (Must match the number of names being created.)' + ) + field_order = ('device_type', 'name_pattern', 'label_pattern', 'position_pattern') + + +class ModuleBayCreateForm(DeviceComponentCreateForm): + position_pattern = ExpandableNameField( + label='Position', + required=False, + help_text='Alphanumeric ranges are supported. (Must match the number of names being created.)' + ) + field_order = ('device', 'name_pattern', 'label_pattern', 'position_pattern') + + class VirtualChassisCreateForm(NetBoxModelForm): region = DynamicModelChoiceField( queryset=Region.objects.all(), diff --git a/netbox/dcim/tables/devices.py b/netbox/dcim/tables/devices.py index a24c48e68..018e35545 100644 --- a/netbox/dcim/tables/devices.py +++ b/netbox/dcim/tables/devices.py @@ -744,8 +744,8 @@ class DeviceModuleBayTable(ModuleBayTable): class Meta(DeviceComponentTable.Meta): model = ModuleBay - fields = ('pk', 'id', 'name', 'label', 'description', 'installed_module', 'tags', 'actions') - default_columns = ('pk', 'name', 'label', 'description', 'installed_module') + fields = ('pk', 'id', 'name', 'label', 'position', 'installed_module', 'description', 'tags', 'actions') + default_columns = ('pk', 'name', 'label', 'installed_module', 'description') class InventoryItemTable(DeviceComponentTable): diff --git a/netbox/dcim/views.py b/netbox/dcim/views.py index be68ff8ed..eb189b457 100644 --- a/netbox/dcim/views.py +++ b/netbox/dcim/views.py @@ -1324,9 +1324,10 @@ class RearPortTemplateBulkDeleteView(generic.BulkDeleteView): class ModuleBayTemplateCreateView(generic.ComponentCreateView): queryset = ModuleBayTemplate.objects.all() - form = forms.ComponentTemplateCreateForm + form = forms.ModuleBayTemplateCreateForm model_form = forms.ModuleBayTemplateForm - template_name = 'dcim/component_template_create.html' + template_name = 'dcim/modulebaytemplate_create.html' + patterned_fields = ('name', 'label', 'position') class ModuleBayTemplateEditView(generic.ObjectEditView): @@ -2304,8 +2305,9 @@ class ModuleBayView(generic.ObjectView): class ModuleBayCreateView(generic.ComponentCreateView): queryset = ModuleBay.objects.all() - form = forms.DeviceComponentCreateForm + form = forms.ModuleBayCreateForm model_form = forms.ModuleBayForm + patterned_fields = ('name', 'label', 'position') class ModuleBayEditView(generic.ObjectEditView): diff --git a/netbox/netbox/views/generic/object_views.py b/netbox/netbox/views/generic/object_views.py index 78fdaacde..eb10d5b07 100644 --- a/netbox/netbox/views/generic/object_views.py +++ b/netbox/netbox/views/generic/object_views.py @@ -587,14 +587,12 @@ class ComponentCreateView(GetReturnURLMixin, BaseObjectView): if form.is_valid(): new_components = [] data = deepcopy(request.POST) - names = form.cleaned_data['name_pattern'] - labels = form.cleaned_data.get('label_pattern') + pattern_count = len(form.cleaned_data[f'{self.patterned_fields[0]}_pattern']) - for i, name in enumerate(names): - label = labels[i] if labels else None - # Initialize the individual component form - data['name'] = name - data['label'] = label + for i in range(pattern_count): + for field_name in self.patterned_fields: + if form.cleaned_data.get(f'{field_name}_pattern'): + data[field_name] = form.cleaned_data[f'{field_name}_pattern'][i] if hasattr(form, 'get_iterative_data'): data.update(form.get_iterative_data(i)) diff --git a/netbox/templates/dcim/modulebaytemplate_create.html b/netbox/templates/dcim/modulebaytemplate_create.html new file mode 100644 index 000000000..74323ac4b --- /dev/null +++ b/netbox/templates/dcim/modulebaytemplate_create.html @@ -0,0 +1,7 @@ +{% extends 'dcim/component_template_create.html' %} +{% load form_helpers %} + +{% block replication_fields %} + {{ block.super }} + {% render_field replication_form.position_pattern %} +{% endblock replication_fields %}