mirror of
https://github.com/netbox-community/netbox.git
synced 2025-07-25 01:48:38 -06:00
Closes #8667: Support position patterning when creating module bays & templates
This commit is contained in:
parent
6604ebfd01
commit
a2fe23549b
@ -8,11 +8,13 @@ from utilities.forms import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
__all__ = (
|
__all__ = (
|
||||||
'ModularComponentTemplateCreateForm',
|
|
||||||
'DeviceComponentCreateForm',
|
|
||||||
'ComponentTemplateCreateForm',
|
'ComponentTemplateCreateForm',
|
||||||
|
'DeviceComponentCreateForm',
|
||||||
'FrontPortCreateForm',
|
'FrontPortCreateForm',
|
||||||
'FrontPortTemplateCreateForm',
|
'FrontPortTemplateCreateForm',
|
||||||
|
'ModularComponentTemplateCreateForm',
|
||||||
|
'ModuleBayCreateForm',
|
||||||
|
'ModuleBayTemplateCreateForm',
|
||||||
'VirtualChassisCreateForm',
|
'VirtualChassisCreateForm',
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -34,14 +36,17 @@ class ComponentCreateForm(BootstrapMixin, forms.Form):
|
|||||||
def clean(self):
|
def clean(self):
|
||||||
super().clean()
|
super().clean()
|
||||||
|
|
||||||
# Validate that the number of components being created from both the name_pattern and label_pattern are equal
|
# Validate that all patterned fields generate an equal number of values
|
||||||
if self.cleaned_data['label_pattern']:
|
patterned_fields = [
|
||||||
name_pattern_count = len(self.cleaned_data['name_pattern'])
|
field_name for field_name in self.fields if field_name.endswith('_pattern')
|
||||||
label_pattern_count = len(self.cleaned_data['label_pattern'])
|
]
|
||||||
if name_pattern_count != label_pattern_count:
|
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({
|
raise forms.ValidationError({
|
||||||
'label_pattern': f'The provided name pattern will create {name_pattern_count} components, however '
|
field_name: f'The provided pattern specifies {value_count} values, but {pattern_count} are '
|
||||||
f'{label_pattern_count} labels will be generated. These counts must match.'
|
f'expected.'
|
||||||
}, code='label_pattern_mismatch')
|
}, 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):
|
class VirtualChassisCreateForm(NetBoxModelForm):
|
||||||
region = DynamicModelChoiceField(
|
region = DynamicModelChoiceField(
|
||||||
queryset=Region.objects.all(),
|
queryset=Region.objects.all(),
|
||||||
|
@ -744,8 +744,8 @@ class DeviceModuleBayTable(ModuleBayTable):
|
|||||||
|
|
||||||
class Meta(DeviceComponentTable.Meta):
|
class Meta(DeviceComponentTable.Meta):
|
||||||
model = ModuleBay
|
model = ModuleBay
|
||||||
fields = ('pk', 'id', 'name', 'label', 'description', 'installed_module', 'tags', 'actions')
|
fields = ('pk', 'id', 'name', 'label', 'position', 'installed_module', 'description', 'tags', 'actions')
|
||||||
default_columns = ('pk', 'name', 'label', 'description', 'installed_module')
|
default_columns = ('pk', 'name', 'label', 'installed_module', 'description')
|
||||||
|
|
||||||
|
|
||||||
class InventoryItemTable(DeviceComponentTable):
|
class InventoryItemTable(DeviceComponentTable):
|
||||||
|
@ -1324,9 +1324,10 @@ class RearPortTemplateBulkDeleteView(generic.BulkDeleteView):
|
|||||||
|
|
||||||
class ModuleBayTemplateCreateView(generic.ComponentCreateView):
|
class ModuleBayTemplateCreateView(generic.ComponentCreateView):
|
||||||
queryset = ModuleBayTemplate.objects.all()
|
queryset = ModuleBayTemplate.objects.all()
|
||||||
form = forms.ComponentTemplateCreateForm
|
form = forms.ModuleBayTemplateCreateForm
|
||||||
model_form = forms.ModuleBayTemplateForm
|
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):
|
class ModuleBayTemplateEditView(generic.ObjectEditView):
|
||||||
@ -2304,8 +2305,9 @@ class ModuleBayView(generic.ObjectView):
|
|||||||
|
|
||||||
class ModuleBayCreateView(generic.ComponentCreateView):
|
class ModuleBayCreateView(generic.ComponentCreateView):
|
||||||
queryset = ModuleBay.objects.all()
|
queryset = ModuleBay.objects.all()
|
||||||
form = forms.DeviceComponentCreateForm
|
form = forms.ModuleBayCreateForm
|
||||||
model_form = forms.ModuleBayForm
|
model_form = forms.ModuleBayForm
|
||||||
|
patterned_fields = ('name', 'label', 'position')
|
||||||
|
|
||||||
|
|
||||||
class ModuleBayEditView(generic.ObjectEditView):
|
class ModuleBayEditView(generic.ObjectEditView):
|
||||||
|
@ -587,14 +587,12 @@ class ComponentCreateView(GetReturnURLMixin, BaseObjectView):
|
|||||||
if form.is_valid():
|
if form.is_valid():
|
||||||
new_components = []
|
new_components = []
|
||||||
data = deepcopy(request.POST)
|
data = deepcopy(request.POST)
|
||||||
names = form.cleaned_data['name_pattern']
|
pattern_count = len(form.cleaned_data[f'{self.patterned_fields[0]}_pattern'])
|
||||||
labels = form.cleaned_data.get('label_pattern')
|
|
||||||
|
|
||||||
for i, name in enumerate(names):
|
for i in range(pattern_count):
|
||||||
label = labels[i] if labels else None
|
for field_name in self.patterned_fields:
|
||||||
# Initialize the individual component form
|
if form.cleaned_data.get(f'{field_name}_pattern'):
|
||||||
data['name'] = name
|
data[field_name] = form.cleaned_data[f'{field_name}_pattern'][i]
|
||||||
data['label'] = label
|
|
||||||
|
|
||||||
if hasattr(form, 'get_iterative_data'):
|
if hasattr(form, 'get_iterative_data'):
|
||||||
data.update(form.get_iterative_data(i))
|
data.update(form.get_iterative_data(i))
|
||||||
|
7
netbox/templates/dcim/modulebaytemplate_create.html
Normal file
7
netbox/templates/dcim/modulebaytemplate_create.html
Normal file
@ -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 %}
|
Loading…
Reference in New Issue
Block a user