From 4988c7b7d2aa686000d3fbc7ff9e8021b6c113a9 Mon Sep 17 00:00:00 2001 From: jeremystretch Date: Wed, 7 Sep 2022 16:57:19 -0400 Subject: [PATCH] Initial work on #10247 --- netbox/dcim/forms/models.py | 99 +++++---- netbox/dcim/forms/object_create.py | 210 +++++++++++------- netbox/dcim/tests/test_forms.py | 4 +- netbox/dcim/views.py | 86 ++----- netbox/netbox/views/generic/object_views.py | 29 +-- .../dcim/component_template_create.html | 38 ---- .../dcim/frontporttemplate_create.html | 7 - .../templates/dcim/inventoryitem_create.html | 17 -- .../dcim/inventoryitemtemplate_create.html | 17 -- .../dcim/modulebaytemplate_create.html | 7 - .../virtualization/vminterface_edit.html | 69 ------ netbox/virtualization/forms/models.py | 12 +- netbox/virtualization/forms/object_create.py | 15 +- netbox/virtualization/views.py | 1 - 14 files changed, 224 insertions(+), 387 deletions(-) delete mode 100644 netbox/templates/dcim/component_template_create.html delete mode 100644 netbox/templates/dcim/frontporttemplate_create.html delete mode 100644 netbox/templates/dcim/inventoryitem_create.html delete mode 100644 netbox/templates/dcim/inventoryitemtemplate_create.html delete mode 100644 netbox/templates/dcim/modulebaytemplate_create.html delete mode 100644 netbox/templates/virtualization/vminterface_edit.html diff --git a/netbox/dcim/forms/models.py b/netbox/dcim/forms/models.py index d1d5b1683..d2d6af4aa 100644 --- a/netbox/dcim/forms/models.py +++ b/netbox/dcim/forms/models.py @@ -986,47 +986,57 @@ class VCMemberSelectForm(BootstrapMixin, forms.Form): # Device component templates # +class ComponentTemplateForm(BootstrapMixin, forms.ModelForm): + device_type = DynamicModelChoiceField( + queryset=DeviceType.objects.all() + ) -class ConsolePortTemplateForm(BootstrapMixin, forms.ModelForm): + +class ModularComponentTemplateForm(BootstrapMixin, forms.ModelForm): + device_type = DynamicModelChoiceField( + queryset=DeviceType.objects.all(), + required=False + ) + module_type = DynamicModelChoiceField( + queryset=ModuleType.objects.all(), + required=False + ) + + +class ConsolePortTemplateForm(ModularComponentTemplateForm): class Meta: model = ConsolePortTemplate fields = [ 'device_type', 'module_type', 'name', 'label', 'type', 'description', ] widgets = { - 'device_type': forms.HiddenInput(), - 'module_type': forms.HiddenInput(), 'type': StaticSelect, } -class ConsoleServerPortTemplateForm(BootstrapMixin, forms.ModelForm): +class ConsoleServerPortTemplateForm(ModularComponentTemplateForm): class Meta: model = ConsoleServerPortTemplate fields = [ 'device_type', 'module_type', 'name', 'label', 'type', 'description', ] widgets = { - 'device_type': forms.HiddenInput(), - 'module_type': forms.HiddenInput(), 'type': StaticSelect, } -class PowerPortTemplateForm(BootstrapMixin, forms.ModelForm): +class PowerPortTemplateForm(ModularComponentTemplateForm): class Meta: model = PowerPortTemplate fields = [ 'device_type', 'module_type', 'name', 'label', 'type', 'maximum_draw', 'allocated_draw', 'description', ] widgets = { - 'device_type': forms.HiddenInput(), - 'module_type': forms.HiddenInput(), 'type': StaticSelect(), } -class PowerOutletTemplateForm(BootstrapMixin, forms.ModelForm): +class PowerOutletTemplateForm(ModularComponentTemplateForm): power_port = DynamicModelChoiceField( queryset=PowerPortTemplate.objects.all(), required=False, @@ -1041,29 +1051,25 @@ class PowerOutletTemplateForm(BootstrapMixin, forms.ModelForm): 'device_type', 'module_type', 'name', 'label', 'type', 'power_port', 'feed_leg', 'description', ] widgets = { - 'device_type': forms.HiddenInput(), - 'module_type': forms.HiddenInput(), 'type': StaticSelect(), 'feed_leg': StaticSelect(), } -class InterfaceTemplateForm(BootstrapMixin, forms.ModelForm): +class InterfaceTemplateForm(ModularComponentTemplateForm): class Meta: model = InterfaceTemplate fields = [ 'device_type', 'module_type', 'name', 'label', 'type', 'mgmt_only', 'description', 'poe_mode', 'poe_type', ] widgets = { - 'device_type': forms.HiddenInput(), - 'module_type': forms.HiddenInput(), 'type': StaticSelect(), 'poe_mode': StaticSelect(), 'poe_type': StaticSelect(), } -class FrontPortTemplateForm(BootstrapMixin, forms.ModelForm): +class FrontPortTemplateForm(ModularComponentTemplateForm): rear_port = DynamicModelChoiceField( queryset=RearPortTemplate.objects.all(), required=False, @@ -1080,48 +1086,38 @@ class FrontPortTemplateForm(BootstrapMixin, forms.ModelForm): 'description', ] widgets = { - 'device_type': forms.HiddenInput(), - 'module_type': forms.HiddenInput(), 'type': StaticSelect(), } -class RearPortTemplateForm(BootstrapMixin, forms.ModelForm): +class RearPortTemplateForm(ModularComponentTemplateForm): class Meta: model = RearPortTemplate fields = [ 'device_type', 'module_type', 'name', 'label', 'type', 'color', 'positions', 'description', ] widgets = { - 'device_type': forms.HiddenInput(), - 'module_type': forms.HiddenInput(), 'type': StaticSelect(), } -class ModuleBayTemplateForm(BootstrapMixin, forms.ModelForm): +class ModuleBayTemplateForm(ComponentTemplateForm): class Meta: model = ModuleBayTemplate fields = [ 'device_type', 'name', 'label', 'position', 'description', ] - widgets = { - 'device_type': forms.HiddenInput(), - } -class DeviceBayTemplateForm(BootstrapMixin, forms.ModelForm): +class DeviceBayTemplateForm(ComponentTemplateForm): class Meta: model = DeviceBayTemplate fields = [ 'device_type', 'name', 'label', 'description', ] - widgets = { - 'device_type': forms.HiddenInput(), - } -class InventoryItemTemplateForm(BootstrapMixin, forms.ModelForm): +class InventoryItemTemplateForm(ComponentTemplateForm): parent = DynamicModelChoiceField( queryset=InventoryItemTemplate.objects.all(), required=False, @@ -1154,9 +1150,6 @@ class InventoryItemTemplateForm(BootstrapMixin, forms.ModelForm): 'device_type', 'parent', 'name', 'label', 'role', 'manufacturer', 'part_id', 'description', 'component_type', 'component_id', ] - widgets = { - 'device_type': forms.HiddenInput(), - } # @@ -1164,6 +1157,9 @@ class InventoryItemTemplateForm(BootstrapMixin, forms.ModelForm): # class ConsolePortForm(NetBoxModelForm): + device = DynamicModelChoiceField( + queryset=Device.objects.all() + ) module = DynamicModelChoiceField( queryset=Module.objects.all(), required=False, @@ -1178,13 +1174,15 @@ class ConsolePortForm(NetBoxModelForm): 'device', 'module', 'name', 'label', 'type', 'speed', 'mark_connected', 'description', 'tags', ] widgets = { - 'device': forms.HiddenInput(), 'type': StaticSelect(), 'speed': StaticSelect(), } class ConsoleServerPortForm(NetBoxModelForm): + device = DynamicModelChoiceField( + queryset=Device.objects.all() + ) module = DynamicModelChoiceField( queryset=Module.objects.all(), required=False, @@ -1199,13 +1197,15 @@ class ConsoleServerPortForm(NetBoxModelForm): 'device', 'module', 'name', 'label', 'type', 'speed', 'mark_connected', 'description', 'tags', ] widgets = { - 'device': forms.HiddenInput(), 'type': StaticSelect(), 'speed': StaticSelect(), } class PowerPortForm(NetBoxModelForm): + device = DynamicModelChoiceField( + queryset=Device.objects.all() + ) module = DynamicModelChoiceField( queryset=Module.objects.all(), required=False, @@ -1222,12 +1222,14 @@ class PowerPortForm(NetBoxModelForm): 'tags', ] widgets = { - 'device': forms.HiddenInput(), 'type': StaticSelect(), } class PowerOutletForm(NetBoxModelForm): + device = DynamicModelChoiceField( + queryset=Device.objects.all() + ) module = DynamicModelChoiceField( queryset=Module.objects.all(), required=False, @@ -1250,13 +1252,15 @@ class PowerOutletForm(NetBoxModelForm): 'tags', ] widgets = { - 'device': forms.HiddenInput(), 'type': StaticSelect(), 'feed_leg': StaticSelect(), } class InterfaceForm(InterfaceCommonForm, NetBoxModelForm): + device = DynamicModelChoiceField( + queryset=Device.objects.all() + ) module = DynamicModelChoiceField( queryset=Module.objects.all(), required=False, @@ -1352,7 +1356,6 @@ class InterfaceForm(InterfaceCommonForm, NetBoxModelForm): 'untagged_vlan', 'tagged_vlans', 'vrf', 'tags', ] widgets = { - 'device': forms.HiddenInput(), 'type': StaticSelect(), 'speed': SelectSpeedWidget(), 'poe_mode': StaticSelect(), @@ -1383,6 +1386,9 @@ class InterfaceForm(InterfaceCommonForm, NetBoxModelForm): class FrontPortForm(NetBoxModelForm): + device = DynamicModelChoiceField( + queryset=Device.objects.all() + ) module = DynamicModelChoiceField( queryset=Module.objects.all(), required=False, @@ -1404,12 +1410,14 @@ class FrontPortForm(NetBoxModelForm): 'description', 'tags', ] widgets = { - 'device': forms.HiddenInput(), 'type': StaticSelect(), } class RearPortForm(NetBoxModelForm): + device = DynamicModelChoiceField( + queryset=Device.objects.all() + ) module = DynamicModelChoiceField( queryset=Module.objects.all(), required=False, @@ -1424,33 +1432,32 @@ class RearPortForm(NetBoxModelForm): 'device', 'module', 'name', 'label', 'type', 'color', 'positions', 'mark_connected', 'description', 'tags', ] widgets = { - 'device': forms.HiddenInput(), 'type': StaticSelect(), } class ModuleBayForm(NetBoxModelForm): + device = DynamicModelChoiceField( + queryset=Device.objects.all() + ) class Meta: model = ModuleBay fields = [ 'device', 'name', 'label', 'position', 'description', 'tags', ] - widgets = { - 'device': forms.HiddenInput(), - } class DeviceBayForm(NetBoxModelForm): + device = DynamicModelChoiceField( + queryset=Device.objects.all() + ) class Meta: model = DeviceBay fields = [ 'device', 'name', 'label', 'description', 'tags', ] - widgets = { - 'device': forms.HiddenInput(), - } class PopulateDeviceBayForm(BootstrapMixin, forms.Form): diff --git a/netbox/dcim/forms/object_create.py b/netbox/dcim/forms/object_create.py index d2c941b34..75283509a 100644 --- a/netbox/dcim/forms/object_create.py +++ b/netbox/dcim/forms/object_create.py @@ -2,103 +2,108 @@ from django import forms from dcim.models import * from netbox.forms import NetBoxModelForm -from utilities.forms import ( - BootstrapMixin, DynamicModelChoiceField, DynamicModelMultipleChoiceField, ExpandableNameField, -) +from utilities.forms import DynamicModelChoiceField, DynamicModelMultipleChoiceField, ExpandableNameField +from . import models as model_forms __all__ = ( - 'ComponentTemplateCreateForm', - 'DeviceComponentCreateForm', + 'ComponentCreateForm', + 'ConsolePortCreateForm', + 'ConsolePortTemplateCreateForm', + 'ConsoleServerPortCreateForm', + 'ConsoleServerPortTemplateCreateForm', + 'DeviceBayCreateForm', + 'DeviceBayTemplateCreateForm', 'FrontPortCreateForm', 'FrontPortTemplateCreateForm', + 'InterfaceCreateForm', + 'InterfaceTemplateCreateForm', 'InventoryItemCreateForm', - 'ModularComponentTemplateCreateForm', + 'InventoryItemTemplateCreateForm', 'ModuleBayCreateForm', 'ModuleBayTemplateCreateForm', + 'PowerOutletCreateForm', + 'PowerOutletTemplateCreateForm', + 'PowerPortCreateForm', + 'PowerPortTemplateCreateForm', + 'RearPortCreateForm', + 'RearPortTemplateCreateForm', 'VirtualChassisCreateForm', ) -class ComponentCreateForm(BootstrapMixin, forms.Form): +class ComponentCreateForm(forms.Form): """ Subclass this form when facilitating the creation of one or more device component or component templates based on a name pattern. """ - name_pattern = ExpandableNameField( - label='Name' - ) - label_pattern = ExpandableNameField( - label='Label', + name = ExpandableNameField() + label = ExpandableNameField( required=False, help_text='Alphanumeric ranges are supported. (Must match the number of names being created.)' ) - def clean(self): - super().clean() - - # 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({ - field_name: f'The provided pattern specifies {value_count} values, but {pattern_count} are ' - f'expected.' - }, code='label_pattern_mismatch') + # TODO: Incorporate this validation + # def clean(self): + # super().clean() + # + # # 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({ + # field_name: f'The provided pattern specifies {value_count} values, but {pattern_count} are ' + # f'expected.' + # }, code='label_pattern_mismatch') -class ComponentTemplateCreateForm(ComponentCreateForm): - """ - Creation form for component templates that can be assigned only to a DeviceType. - """ - device_type = DynamicModelChoiceField( - queryset=DeviceType.objects.all(), - ) - field_order = ('device_type', 'name_pattern', 'label_pattern') +# class ModularComponentTemplateCreateForm(ComponentCreateForm): +# """ +# Creation form for component templates that can be assigned to either a DeviceType *or* a ModuleType. +# """ +# name = ExpandableNameField( +# label='Name', +# help_text=""" +# Alphanumeric ranges are supported for bulk creation. Mixed cases and types within a single range +# are not supported. Example: [ge,xe]-0/0/[0-9]. {module} is accepted as a substitution for +# the module bay position. +# """ +# ) -class ModularComponentTemplateCreateForm(ComponentCreateForm): - """ - Creation form for component templates that can be assigned to either a DeviceType *or* a ModuleType. - """ - name_pattern = ExpandableNameField( - label='Name', - help_text=""" - Alphanumeric ranges are supported for bulk creation. Mixed cases and types within a single range - are not supported. Example: [ge,xe]-0/0/[0-9]. {module} is accepted as a substitution for - the module bay position. - """ - ) - device_type = DynamicModelChoiceField( - queryset=DeviceType.objects.all(), - required=False - ) - module_type = DynamicModelChoiceField( - queryset=ModuleType.objects.all(), - required=False - ) - field_order = ('device_type', 'module_type', 'name_pattern', 'label_pattern') +# +# Device component templates +# + +class ConsolePortTemplateCreateForm(ComponentCreateForm, model_forms.ConsolePortTemplateForm): + pass -class DeviceComponentCreateForm(ComponentCreateForm): - device = DynamicModelChoiceField( - queryset=Device.objects.all() - ) - field_order = ('device', 'name_pattern', 'label_pattern') +class ConsoleServerPortTemplateCreateForm(ComponentCreateForm, model_forms.ConsoleServerPortTemplateForm): + pass -class FrontPortTemplateCreateForm(ModularComponentTemplateCreateForm): +class PowerPortTemplateCreateForm(ComponentCreateForm, model_forms.PowerPortTemplateForm): + pass + + +class PowerOutletTemplateCreateForm(ComponentCreateForm, model_forms.PowerOutletTemplateForm): + pass + + +class InterfaceTemplateCreateForm(ComponentCreateForm, model_forms.InterfaceTemplateForm): + pass + + +class FrontPortTemplateCreateForm(ComponentCreateForm, model_forms.FrontPortTemplateForm): rear_port_set = forms.MultipleChoiceField( choices=[], label='Rear ports', help_text='Select one rear port assignment for each front port being created.', ) - field_order = ( - 'device_type', 'name_pattern', 'label_pattern', 'rear_port_set', - ) def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) @@ -143,15 +148,56 @@ class FrontPortTemplateCreateForm(ModularComponentTemplateCreateForm): } -class FrontPortCreateForm(DeviceComponentCreateForm): +class RearPortTemplateCreateForm(ComponentCreateForm, model_forms.RearPortTemplateForm): + pass + + +class DeviceBayTemplateCreateForm(ComponentCreateForm, model_forms.DeviceBayTemplateForm): + pass + + +class ModuleBayTemplateCreateForm(ComponentCreateForm, model_forms.ModuleBayTemplateForm): + position = ExpandableNameField( + label='Position', + required=False, + help_text='Alphanumeric ranges are supported. (Must match the number of names being created.)' + ) + + +class InventoryItemTemplateCreateForm(ComponentCreateForm, model_forms.InventoryItemTemplateForm): + pass + + +# +# Device components +# + +class ConsolePortCreateForm(ComponentCreateForm, model_forms.ConsolePortForm): + pass + + +class ConsoleServerPortCreateForm(ComponentCreateForm, model_forms.ConsoleServerPortForm): + pass + + +class PowerPortCreateForm(ComponentCreateForm, model_forms.PowerPortForm): + pass + + +class PowerOutletCreateForm(ComponentCreateForm, model_forms.PowerOutletForm): + pass + + +class InterfaceCreateForm(ComponentCreateForm, model_forms.InterfaceForm): + pass + + +class FrontPortCreateForm(ComponentCreateForm, model_forms.FrontPortForm): rear_port_set = forms.MultipleChoiceField( choices=[], label='Rear ports', help_text='Select one rear port assignment for each front port being created.', ) - field_order = ( - 'device', 'name_pattern', 'label_pattern', 'rear_port_set', - ) def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) @@ -189,22 +235,20 @@ class FrontPortCreateForm(DeviceComponentCreateForm): } -class ModuleBayTemplateCreateForm(ComponentTemplateCreateForm): - position_pattern = ExpandableNameField( +class RearPortCreateForm(ComponentCreateForm, model_forms.RearPortForm): + pass + + +class DeviceBayCreateForm(ComponentCreateForm, model_forms.DeviceBayForm): + pass + + +class ModuleBayCreateForm(ComponentCreateForm, model_forms.ModuleBayForm): + position = 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 InventoryItemCreateForm(ComponentCreateForm): @@ -212,6 +256,10 @@ class InventoryItemCreateForm(ComponentCreateForm): field_order = ('name_pattern', 'label_pattern') +# +# Virtual chassis +# + class VirtualChassisCreateForm(NetBoxModelForm): region = DynamicModelChoiceField( queryset=Region.objects.all(), diff --git a/netbox/dcim/tests/test_forms.py b/netbox/dcim/tests/test_forms.py index 53474314f..aad282725 100644 --- a/netbox/dcim/tests/test_forms.py +++ b/netbox/dcim/tests/test_forms.py @@ -132,7 +132,7 @@ class LabelTestCase(TestCase): 'name_pattern': 'eth[0-9]', 'label_pattern': 'Interface[0-9]', } - form = DeviceComponentCreateForm(interface_data) + form = InterfaceCreateForm(interface_data) self.assertTrue(form.is_valid()) @@ -145,7 +145,7 @@ class LabelTestCase(TestCase): 'name_pattern': 'eth[0-9]', 'label_pattern': 'Interface[0-1]', } - form = DeviceComponentCreateForm(bad_interface_data) + form = InterfaceCreateForm(bad_interface_data) self.assertFalse(form.is_valid()) self.assertIn('label_pattern', form.errors) diff --git a/netbox/dcim/views.py b/netbox/dcim/views.py index 6ee74377a..a8f710efe 100644 --- a/netbox/dcim/views.py +++ b/netbox/dcim/views.py @@ -1120,9 +1120,8 @@ class ModuleTypeBulkDeleteView(generic.BulkDeleteView): class ConsolePortTemplateCreateView(generic.ComponentCreateView): queryset = ConsolePortTemplate.objects.all() - form = forms.ModularComponentTemplateCreateForm + form = forms.ConsolePortTemplateCreateForm model_form = forms.ConsolePortTemplateForm - template_name = 'dcim/component_template_create.html' class ConsolePortTemplateEditView(generic.ObjectEditView): @@ -1155,9 +1154,8 @@ class ConsolePortTemplateBulkDeleteView(generic.BulkDeleteView): class ConsoleServerPortTemplateCreateView(generic.ComponentCreateView): queryset = ConsoleServerPortTemplate.objects.all() - form = forms.ModularComponentTemplateCreateForm + form = forms.ConsoleServerPortTemplateCreateForm model_form = forms.ConsoleServerPortTemplateForm - template_name = 'dcim/component_template_create.html' class ConsoleServerPortTemplateEditView(generic.ObjectEditView): @@ -1190,9 +1188,8 @@ class ConsoleServerPortTemplateBulkDeleteView(generic.BulkDeleteView): class PowerPortTemplateCreateView(generic.ComponentCreateView): queryset = PowerPortTemplate.objects.all() - form = forms.ModularComponentTemplateCreateForm + form = forms.PowerPortTemplateCreateForm model_form = forms.PowerPortTemplateForm - template_name = 'dcim/component_template_create.html' class PowerPortTemplateEditView(generic.ObjectEditView): @@ -1225,9 +1222,8 @@ class PowerPortTemplateBulkDeleteView(generic.BulkDeleteView): class PowerOutletTemplateCreateView(generic.ComponentCreateView): queryset = PowerOutletTemplate.objects.all() - form = forms.ModularComponentTemplateCreateForm + form = forms.PowerOutletTemplateCreateForm model_form = forms.PowerOutletTemplateForm - template_name = 'dcim/component_template_create.html' class PowerOutletTemplateEditView(generic.ObjectEditView): @@ -1260,9 +1256,8 @@ class PowerOutletTemplateBulkDeleteView(generic.BulkDeleteView): class InterfaceTemplateCreateView(generic.ComponentCreateView): queryset = InterfaceTemplate.objects.all() - form = forms.ModularComponentTemplateCreateForm + form = forms.InterfaceTemplateCreateForm model_form = forms.InterfaceTemplateForm - template_name = 'dcim/component_template_create.html' class InterfaceTemplateEditView(generic.ObjectEditView): @@ -1297,15 +1292,6 @@ class FrontPortTemplateCreateView(generic.ComponentCreateView): queryset = FrontPortTemplate.objects.all() form = forms.FrontPortTemplateCreateForm model_form = forms.FrontPortTemplateForm - template_name = 'dcim/frontporttemplate_create.html' - - 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): @@ -1338,9 +1324,8 @@ class FrontPortTemplateBulkDeleteView(generic.BulkDeleteView): class RearPortTemplateCreateView(generic.ComponentCreateView): queryset = RearPortTemplate.objects.all() - form = forms.ModularComponentTemplateCreateForm + form = forms.RearPortTemplateCreateForm model_form = forms.RearPortTemplateForm - template_name = 'dcim/component_template_create.html' class RearPortTemplateEditView(generic.ObjectEditView): @@ -1375,7 +1360,6 @@ class ModuleBayTemplateCreateView(generic.ComponentCreateView): queryset = ModuleBayTemplate.objects.all() form = forms.ModuleBayTemplateCreateForm model_form = forms.ModuleBayTemplateForm - template_name = 'dcim/modulebaytemplate_create.html' patterned_fields = ('name', 'label', 'position') @@ -1409,9 +1393,8 @@ class ModuleBayTemplateBulkDeleteView(generic.BulkDeleteView): class DeviceBayTemplateCreateView(generic.ComponentCreateView): queryset = DeviceBayTemplate.objects.all() - form = forms.ComponentTemplateCreateForm + form = forms.DeviceBayTemplateCreateForm model_form = forms.DeviceBayTemplateForm - template_name = 'dcim/component_template_create.html' class DeviceBayTemplateEditView(generic.ObjectEditView): @@ -1444,9 +1427,8 @@ class DeviceBayTemplateBulkDeleteView(generic.BulkDeleteView): class InventoryItemTemplateCreateView(generic.ComponentCreateView): queryset = InventoryItemTemplate.objects.all() - form = forms.ModularComponentTemplateCreateForm + form = forms.InventoryItemTemplateCreateForm model_form = forms.InventoryItemTemplateForm - template_name = 'dcim/inventoryitemtemplate_create.html' def alter_object(self, instance, request): # Set component (if any) @@ -1874,7 +1856,7 @@ class ConsolePortView(generic.ObjectView): class ConsolePortCreateView(generic.ComponentCreateView): queryset = ConsolePort.objects.all() - form = forms.DeviceComponentCreateForm + form = forms.ConsolePortCreateForm model_form = forms.ConsolePortForm @@ -1933,7 +1915,7 @@ class ConsoleServerPortView(generic.ObjectView): class ConsoleServerPortCreateView(generic.ComponentCreateView): queryset = ConsoleServerPort.objects.all() - form = forms.DeviceComponentCreateForm + form = forms.ConsoleServerPortCreateForm model_form = forms.ConsoleServerPortForm @@ -1992,7 +1974,7 @@ class PowerPortView(generic.ObjectView): class PowerPortCreateView(generic.ComponentCreateView): queryset = PowerPort.objects.all() - form = forms.DeviceComponentCreateForm + form = forms.PowerPortCreateForm model_form = forms.PowerPortForm @@ -2051,7 +2033,7 @@ class PowerOutletView(generic.ObjectView): class PowerOutletCreateView(generic.ComponentCreateView): queryset = PowerOutlet.objects.all() - form = forms.DeviceComponentCreateForm + form = forms.PowerOutletCreateForm model_form = forms.PowerOutletForm @@ -2154,42 +2136,13 @@ class InterfaceView(generic.ObjectView): class InterfaceCreateView(generic.ComponentCreateView): queryset = Interface.objects.all() - form = forms.DeviceComponentCreateForm + form = forms.InterfaceCreateForm model_form = forms.InterfaceForm - # template_name = 'dcim/interface_create.html' - - # TODO: Figure out what to do with this - # def post(self, request): - # """ - # Override inherited post() method to handle request to assign newly created - # interface objects (first object) to an IP Address object. - # """ - # form = self.form(request.POST, initial=request.GET) - # new_objs = self.validate_form(request, form) - # - # if form.is_valid() and not form.errors: - # if '_addanother' in request.POST: - # return redirect(request.get_full_path()) - # elif new_objs is not None and '_assignip' in request.POST and len(new_objs) >= 1 and \ - # request.user.has_perm('ipam.add_ipaddress'): - # first_obj = new_objs[0].pk - # return redirect( - # f'/ipam/ip-addresses/add/?interface={first_obj}&return_url={self.get_return_url(request)}' - # ) - # else: - # return redirect(self.get_return_url(request)) - # - # return render(request, self.template_name, { - # 'obj_type': self.queryset.model._meta.verbose_name, - # 'form': form, - # 'return_url': self.get_return_url(request), - # }) class InterfaceEditView(generic.ObjectEditView): queryset = Interface.objects.all() form = forms.InterfaceForm - template_name = 'dcim/interface_edit.html' class InterfaceDeleteView(generic.ObjectDeleteView): @@ -2244,14 +2197,6 @@ class FrontPortCreateView(generic.ComponentCreateView): 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() @@ -2308,7 +2253,7 @@ class RearPortView(generic.ObjectView): class RearPortCreateView(generic.ComponentCreateView): queryset = RearPort.objects.all() - form = forms.DeviceComponentCreateForm + form = forms.RearPortCreateForm model_form = forms.RearPortForm @@ -2423,7 +2368,7 @@ class DeviceBayView(generic.ObjectView): class DeviceBayCreateView(generic.ComponentCreateView): queryset = DeviceBay.objects.all() - form = forms.DeviceComponentCreateForm + form = forms.DeviceBayCreateForm model_form = forms.DeviceBayForm @@ -2552,7 +2497,6 @@ class InventoryItemCreateView(generic.ComponentCreateView): queryset = InventoryItem.objects.all() form = forms.InventoryItemCreateForm model_form = forms.InventoryItemForm - template_name = 'dcim/inventoryitem_create.html' def alter_object(self, instance, request): # Set component (if any) diff --git a/netbox/netbox/views/generic/object_views.py b/netbox/netbox/views/generic/object_views.py index 7617e0402..c69e04601 100644 --- a/netbox/netbox/views/generic/object_views.py +++ b/netbox/netbox/views/generic/object_views.py @@ -538,7 +538,7 @@ class ComponentCreateView(GetReturnURLMixin, BaseObjectView): """ Add one or more components (e.g. interfaces, console ports, etc.) to a Device or VirtualMachine. """ - template_name = 'dcim/component_create.html' + template_name = 'generic/object_edit.html' form = None model_form = None patterned_fields = ('name', 'label') @@ -549,44 +549,38 @@ class ComponentCreateView(GetReturnURLMixin, BaseObjectView): def alter_object(self, instance, request): return instance - def initialize_forms(self, request): + def initialize_form(self, request): data = request.POST if request.method == 'POST' else None initial_data = normalize_querydict(request.GET) - form = self.form(data=data, initial=request.GET) - model_form = self.model_form(data=data, initial=initial_data) + form = self.form(data=data, initial=initial_data) - # These fields will be set from the pattern values - for field_name in self.patterned_fields: - model_form.fields[field_name].widget = HiddenInput() - - return form, model_form + return form def get(self, request): - form, model_form = self.initialize_forms(request) + form = self.initialize_form(request) instance = self.alter_object(self.queryset.model(), request) return render(request, self.template_name, { 'object': instance, - 'replication_form': form, - 'form': model_form, + 'form': form, 'return_url': self.get_return_url(request), }) def post(self, request): logger = logging.getLogger('netbox.views.ComponentCreateView') - form, model_form = self.initialize_forms(request) + form = self.initialize_form(request) instance = self.alter_object(self.queryset.model(), request) if form.is_valid(): new_components = [] data = deepcopy(request.POST) - pattern_count = len(form.cleaned_data[f'{self.patterned_fields[0]}_pattern']) + pattern_count = len(form.cleaned_data[self.patterned_fields[0]]) 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 form.cleaned_data.get(field_name): + data[field_name] = form.cleaned_data[field_name][i] if hasattr(form, 'get_iterative_data'): data.update(form.get_iterative_data(i)) @@ -626,7 +620,6 @@ class ComponentCreateView(GetReturnURLMixin, BaseObjectView): return render(request, self.template_name, { 'object': instance, - 'replication_form': form, - 'form': model_form, + 'form': form, 'return_url': self.get_return_url(request), }) diff --git a/netbox/templates/dcim/component_template_create.html b/netbox/templates/dcim/component_template_create.html deleted file mode 100644 index d164db872..000000000 --- a/netbox/templates/dcim/component_template_create.html +++ /dev/null @@ -1,38 +0,0 @@ -{% extends 'generic/object_edit.html' %} -{% load form_helpers %} - -{% block form %} - {% if form.module_type %} -
-
- -
-
-
-
- {% render_field replication_form.device_type %} -
-
- {% render_field replication_form.module_type %} -
-
- {% else %} - {% render_field replication_form.device_type %} - {% endif %} - {% block replication_fields %} - {% render_field replication_form.name_pattern %} - {% render_field replication_form.label_pattern %} - {% endblock replication_fields %} - {{ block.super }} -{% endblock form %} diff --git a/netbox/templates/dcim/frontporttemplate_create.html b/netbox/templates/dcim/frontporttemplate_create.html deleted file mode 100644 index 50e9d355c..000000000 --- a/netbox/templates/dcim/frontporttemplate_create.html +++ /dev/null @@ -1,7 +0,0 @@ -{% extends 'dcim/component_template_create.html' %} -{% load form_helpers %} - -{% block replication_fields %} - {{ block.super }} - {% render_field replication_form.rear_port_set %} -{% endblock replication_fields %} diff --git a/netbox/templates/dcim/inventoryitem_create.html b/netbox/templates/dcim/inventoryitem_create.html deleted file mode 100644 index be910f143..000000000 --- a/netbox/templates/dcim/inventoryitem_create.html +++ /dev/null @@ -1,17 +0,0 @@ -{% extends 'dcim/component_create.html' %} -{% load helpers %} -{% load form_helpers %} - -{% block replication_fields %} - {{ block.super }} - {% if object.component %} -
- -
- -
-
- {% endif %} -{% endblock replication_fields %} diff --git a/netbox/templates/dcim/inventoryitemtemplate_create.html b/netbox/templates/dcim/inventoryitemtemplate_create.html deleted file mode 100644 index 9180cf6ab..000000000 --- a/netbox/templates/dcim/inventoryitemtemplate_create.html +++ /dev/null @@ -1,17 +0,0 @@ -{% extends 'dcim/component_template_create.html' %} -{% load helpers %} -{% load form_helpers %} - -{% block replication_fields %} - {{ block.super }} - {% if object.component %} -
- -
- -
-
- {% endif %} -{% endblock replication_fields %} diff --git a/netbox/templates/dcim/modulebaytemplate_create.html b/netbox/templates/dcim/modulebaytemplate_create.html deleted file mode 100644 index 74323ac4b..000000000 --- a/netbox/templates/dcim/modulebaytemplate_create.html +++ /dev/null @@ -1,7 +0,0 @@ -{% extends 'dcim/component_template_create.html' %} -{% load form_helpers %} - -{% block replication_fields %} - {{ block.super }} - {% render_field replication_form.position_pattern %} -{% endblock replication_fields %} diff --git a/netbox/templates/virtualization/vminterface_edit.html b/netbox/templates/virtualization/vminterface_edit.html deleted file mode 100644 index efb138954..000000000 --- a/netbox/templates/virtualization/vminterface_edit.html +++ /dev/null @@ -1,69 +0,0 @@ -{% extends 'generic/object_edit.html' %} -{% load form_helpers %} - -{% block form %} - {# Render hidden fields #} - {% for field in form.hidden_fields %} - {{ field }} - {% endfor %} - -
-
-
Interface
-
- {% if form.instance.virtual_machine %} -
- -
- -
-
- {% endif %} - {% render_field form.name %} - {% render_field form.description %} - {% render_field form.tags %} -
- -
-
-
Addressing
-
- {% render_field form.vrf %} - {% render_field form.mac_address %} -
- -
-
-
Operation
-
- {% render_field form.mtu %} - {% render_field form.enabled %} -
- -
-
-
Related Interfaces
-
- {% render_field form.parent %} - {% render_field form.bridge %} -
- -
-
-
802.1Q Switching
-
- {% render_field form.mode %} - {% render_field form.vlan_group %} - {% render_field form.untagged_vlan %} - {% render_field form.tagged_vlans %} -
- - {% if form.custom_fields %} -
-
-
Custom Fields
-
- {% render_custom_fields form %} -
- {% endif %} -{% endblock %} diff --git a/netbox/virtualization/forms/models.py b/netbox/virtualization/forms/models.py index fca9c6b56..268afb9bb 100644 --- a/netbox/virtualization/forms/models.py +++ b/netbox/virtualization/forms/models.py @@ -5,7 +5,6 @@ from django.core.exceptions import ValidationError from dcim.forms.common import InterfaceCommonForm from dcim.forms.models import INTERFACE_MODE_HELP_TEXT from dcim.models import Device, DeviceRole, Platform, Rack, Region, Site, SiteGroup -from extras.models import Tag from ipam.models import IPAddress, VLAN, VLANGroup, VRF from netbox.forms import NetBoxModelForm from tenancy.forms import TenancyForm @@ -278,6 +277,9 @@ class VirtualMachineForm(TenancyForm, NetBoxModelForm): class VMInterfaceForm(InterfaceCommonForm, NetBoxModelForm): + virtual_machine = DynamicModelChoiceField( + queryset=VirtualMachine.objects.all() + ) parent = DynamicModelChoiceField( queryset=VMInterface.objects.all(), required=False, @@ -338,7 +340,6 @@ class VMInterfaceForm(InterfaceCommonForm, NetBoxModelForm): 'vlan_group', 'untagged_vlan', 'tagged_vlans', 'vrf', 'tags', ] widgets = { - 'virtual_machine': forms.HiddenInput(), 'mode': StaticSelect() } labels = { @@ -347,3 +348,10 @@ class VMInterfaceForm(InterfaceCommonForm, NetBoxModelForm): help_texts = { 'mode': INTERFACE_MODE_HELP_TEXT, } + + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + + # Disable reassignment of VirtualMachine when editing an existing instance + if self.instance.pk: + self.fields['virtual_machine'].disabled = True diff --git a/netbox/virtualization/forms/object_create.py b/netbox/virtualization/forms/object_create.py index feab3bb3a..7b4e55955 100644 --- a/netbox/virtualization/forms/object_create.py +++ b/netbox/virtualization/forms/object_create.py @@ -1,17 +1,10 @@ -from django import forms - -from utilities.forms import BootstrapMixin, DynamicModelChoiceField, ExpandableNameField -from .models import VirtualMachine +from utilities.forms import ExpandableNameField +from .models import VMInterfaceForm __all__ = ( 'VMInterfaceCreateForm', ) -class VMInterfaceCreateForm(BootstrapMixin, forms.Form): - virtual_machine = DynamicModelChoiceField( - queryset=VirtualMachine.objects.all() - ) - name_pattern = ExpandableNameField( - label='Name' - ) +class VMInterfaceCreateForm(VMInterfaceForm): + name = ExpandableNameField() diff --git a/netbox/virtualization/views.py b/netbox/virtualization/views.py index 5b26f8503..1d8db3700 100644 --- a/netbox/virtualization/views.py +++ b/netbox/virtualization/views.py @@ -457,7 +457,6 @@ class VMInterfaceCreateView(generic.ComponentCreateView): class VMInterfaceEditView(generic.ObjectEditView): queryset = VMInterface.objects.all() form = forms.VMInterfaceForm - template_name = 'virtualization/vminterface_edit.html' class VMInterfaceDeleteView(generic.ObjectDeleteView):