Bladeren bron

Introduce model-specific bulk create forms for device components

Jeremy Stretch 5 jaren geleden
bovenliggende
commit
97b8e73716
3 gewijzigde bestanden met toevoegingen van 155 en 97 verwijderingen
  1. 139 91
      netbox/dcim/forms.py
  2. 6 6
      netbox/dcim/views.py
  3. 10 0
      netbox/utilities/forms.py

+ 139 - 91
netbox/dcim/forms.py

@@ -23,8 +23,9 @@ from tenancy.models import Tenant, TenantGroup
 from utilities.forms import (
 from utilities.forms import (
     APISelect, APISelectMultiple, add_blank_choice, ArrayFieldSelectMultiple, BootstrapMixin, BulkEditForm,
     APISelect, APISelectMultiple, add_blank_choice, ArrayFieldSelectMultiple, BootstrapMixin, BulkEditForm,
     BulkEditNullBooleanSelect, ColorSelect, CommentField, ConfirmationForm, CSVChoiceField, DynamicModelChoiceField,
     BulkEditNullBooleanSelect, ColorSelect, CommentField, ConfirmationForm, CSVChoiceField, DynamicModelChoiceField,
-    DynamicModelMultipleChoiceField, ExpandableNameField, FlexibleModelChoiceField, JSONField, SelectWithPK,
-    SmallTextarea, SlugField, StaticSelect2, StaticSelect2Multiple, TagFilterField, BOOLEAN_WITH_BLANK_CHOICES,
+    DynamicModelMultipleChoiceField, ExpandableNameField, FlexibleModelChoiceField, form_from_model, JSONField,
+    SelectWithPK, SmallTextarea, SlugField, StaticSelect2, StaticSelect2Multiple, TagFilterField,
+    BOOLEAN_WITH_BLANK_CHOICES,
 )
 )
 from virtualization.models import Cluster, ClusterGroup, VirtualMachine
 from virtualization.models import Cluster, ClusterGroup, VirtualMachine
 from .choices import *
 from .choices import *
@@ -2299,31 +2300,6 @@ class DeviceBulkAddComponentForm(BootstrapMixin, forms.Form):
     )
     )
 
 
 
 
-class DeviceBulkAddInterfaceForm(DeviceBulkAddComponentForm):
-    type = forms.ChoiceField(
-        choices=InterfaceTypeChoices,
-        widget=StaticSelect2()
-    )
-    enabled = forms.BooleanField(
-        required=False,
-        initial=True
-    )
-    mtu = forms.IntegerField(
-        required=False,
-        min_value=INTERFACE_MTU_MIN,
-        max_value=INTERFACE_MTU_MAX,
-        label='MTU'
-    )
-    mgmt_only = forms.BooleanField(
-        required=False,
-        label='Management only'
-    )
-    description = forms.CharField(
-        max_length=100,
-        required=False
-    )
-
-
 #
 #
 # Console ports
 # Console ports
 #
 #
@@ -2375,6 +2351,15 @@ class ConsolePortCreateForm(BootstrapMixin, forms.Form):
     )
     )
 
 
 
 
+class ConsolePortBulkCreateForm(
+    form_from_model(ConsolePort, ['type', 'description', 'tags']),
+    DeviceBulkAddComponentForm
+):
+    tags = TagField(
+        required=False
+    )
+
+
 class ConsolePortBulkEditForm(BootstrapMixin, AddRemoveTagsForm, BulkEditForm):
 class ConsolePortBulkEditForm(BootstrapMixin, AddRemoveTagsForm, BulkEditForm):
     pk = forms.ModelMultipleChoiceField(
     pk = forms.ModelMultipleChoiceField(
         queryset=ConsolePort.objects.all(),
         queryset=ConsolePort.objects.all(),
@@ -2462,6 +2447,15 @@ class ConsoleServerPortCreateForm(BootstrapMixin, forms.Form):
     )
     )
 
 
 
 
+class ConsoleServerPortBulkCreateForm(
+    form_from_model(ConsoleServerPort, ['type', 'description', 'tags']),
+    DeviceBulkAddComponentForm
+):
+    tags = TagField(
+        required=False
+    )
+
+
 class ConsoleServerPortBulkEditForm(BootstrapMixin, AddRemoveTagsForm, BulkEditForm):
 class ConsoleServerPortBulkEditForm(BootstrapMixin, AddRemoveTagsForm, BulkEditForm):
     pk = forms.ModelMultipleChoiceField(
     pk = forms.ModelMultipleChoiceField(
         queryset=ConsoleServerPort.objects.all(),
         queryset=ConsoleServerPort.objects.all(),
@@ -2573,6 +2567,15 @@ class PowerPortCreateForm(BootstrapMixin, forms.Form):
     )
     )
 
 
 
 
+class PowerPortBulkCreateForm(
+    form_from_model(PowerPort, ['type', 'maximum_draw', 'allocated_draw', 'description', 'tags']),
+    DeviceBulkAddComponentForm
+):
+    tags = TagField(
+        required=False
+    )
+
+
 class PowerPortBulkEditForm(BootstrapMixin, AddRemoveTagsForm, BulkEditForm):
 class PowerPortBulkEditForm(BootstrapMixin, AddRemoveTagsForm, BulkEditForm):
     pk = forms.ModelMultipleChoiceField(
     pk = forms.ModelMultipleChoiceField(
         queryset=PowerPort.objects.all(),
         queryset=PowerPort.objects.all(),
@@ -2700,6 +2703,15 @@ class PowerOutletCreateForm(BootstrapMixin, forms.Form):
         self.fields['power_port'].queryset = PowerPort.objects.filter(device=device)
         self.fields['power_port'].queryset = PowerPort.objects.filter(device=device)
 
 
 
 
+class PowerOutletBulkCreateForm(
+    form_from_model(PowerOutlet, ['type', 'feed_leg', 'description', 'tags']),
+    DeviceBulkAddComponentForm
+):
+    tags = TagField(
+        required=False
+    )
+
+
 class PowerOutletCSVForm(forms.ModelForm):
 class PowerOutletCSVForm(forms.ModelForm):
     device = FlexibleModelChoiceField(
     device = FlexibleModelChoiceField(
         queryset=Device.objects.all(),
         queryset=Device.objects.all(),
@@ -2985,72 +2997,14 @@ class InterfaceCreateForm(BootstrapMixin, InterfaceCommonForm, forms.Form):
         self.fields['tagged_vlans'].widget.add_additional_query_param('site_id', device.site.pk)
         self.fields['tagged_vlans'].widget.add_additional_query_param('site_id', device.site.pk)
 
 
 
 
-class InterfaceCSVForm(forms.ModelForm):
-    device = FlexibleModelChoiceField(
-        queryset=Device.objects.all(),
-        required=False,
-        to_field_name='name',
-        help_text='Name or ID of device',
-        error_messages={
-            'invalid_choice': 'Device not found.',
-        }
-    )
-    virtual_machine = FlexibleModelChoiceField(
-        queryset=VirtualMachine.objects.all(),
-        required=False,
-        to_field_name='name',
-        help_text='Name or ID of virtual machine',
-        error_messages={
-            'invalid_choice': 'Virtual machine not found.',
-        }
-    )
-    lag = FlexibleModelChoiceField(
-        queryset=Interface.objects.all(),
-        required=False,
-        to_field_name='name',
-        help_text='Name or ID of LAG interface',
-        error_messages={
-            'invalid_choice': 'LAG interface not found.',
-        }
-    )
-    type = CSVChoiceField(
-        choices=InterfaceTypeChoices,
-    )
-    mode = CSVChoiceField(
-        choices=InterfaceModeChoices,
-        required=False,
+class InterfaceBulkCreateForm(
+    form_from_model(Interface, ['type', 'enabled', 'mtu', 'mgmt_only', 'description', 'tags']),
+    DeviceBulkAddComponentForm
+):
+    tags = TagField(
+        required=False
     )
     )
 
 
-    class Meta:
-        model = Interface
-        fields = Interface.csv_headers
-
-    def __init__(self, *args, **kwargs):
-        super().__init__(*args, **kwargs)
-
-        # Limit LAG choices to interfaces belonging to this device (or VC master)
-        if self.is_bound and 'device' in self.data:
-            try:
-                device = self.fields['device'].to_python(self.data['device'])
-            except forms.ValidationError:
-                device = None
-        else:
-            device = self.instance.device
-
-        if device:
-            self.fields['lag'].queryset = Interface.objects.filter(
-                device__in=[device, device.get_vc_master()], type=InterfaceTypeChoices.TYPE_LAG
-            )
-        else:
-            self.fields['lag'].queryset = Interface.objects.none()
-
-    def clean_enabled(self):
-        # Make sure enabled is True when it's not included in the uploaded data
-        if 'enabled' not in self.data:
-            return True
-        else:
-            return self.cleaned_data['enabled']
-
 
 
 class InterfaceBulkEditForm(BootstrapMixin, AddRemoveTagsForm, BulkEditForm):
 class InterfaceBulkEditForm(BootstrapMixin, AddRemoveTagsForm, BulkEditForm):
     pk = forms.ModelMultipleChoiceField(
     pk = forms.ModelMultipleChoiceField(
@@ -3175,6 +3129,73 @@ class InterfaceBulkDisconnectForm(ConfirmationForm):
     )
     )
 
 
 
 
+class InterfaceCSVForm(forms.ModelForm):
+    device = FlexibleModelChoiceField(
+        queryset=Device.objects.all(),
+        required=False,
+        to_field_name='name',
+        help_text='Name or ID of device',
+        error_messages={
+            'invalid_choice': 'Device not found.',
+        }
+    )
+    virtual_machine = FlexibleModelChoiceField(
+        queryset=VirtualMachine.objects.all(),
+        required=False,
+        to_field_name='name',
+        help_text='Name or ID of virtual machine',
+        error_messages={
+            'invalid_choice': 'Virtual machine not found.',
+        }
+    )
+    lag = FlexibleModelChoiceField(
+        queryset=Interface.objects.all(),
+        required=False,
+        to_field_name='name',
+        help_text='Name or ID of LAG interface',
+        error_messages={
+            'invalid_choice': 'LAG interface not found.',
+        }
+    )
+    type = CSVChoiceField(
+        choices=InterfaceTypeChoices,
+    )
+    mode = CSVChoiceField(
+        choices=InterfaceModeChoices,
+        required=False,
+    )
+
+    class Meta:
+        model = Interface
+        fields = Interface.csv_headers
+
+    def __init__(self, *args, **kwargs):
+        super().__init__(*args, **kwargs)
+
+        # Limit LAG choices to interfaces belonging to this device (or VC master)
+        if self.is_bound and 'device' in self.data:
+            try:
+                device = self.fields['device'].to_python(self.data['device'])
+            except forms.ValidationError:
+                device = None
+        else:
+            device = self.instance.device
+
+        if device:
+            self.fields['lag'].queryset = Interface.objects.filter(
+                device__in=[device, device.get_vc_master()], type=InterfaceTypeChoices.TYPE_LAG
+            )
+        else:
+            self.fields['lag'].queryset = Interface.objects.none()
+
+    def clean_enabled(self):
+        # Make sure enabled is True when it's not included in the uploaded data
+        if 'enabled' not in self.data:
+            return True
+        else:
+            return self.cleaned_data['enabled']
+
+
 #
 #
 # Front pass-through ports
 # Front pass-through ports
 #
 #
@@ -3331,6 +3352,15 @@ class FrontPortCSVForm(forms.ModelForm):
             self.fields['rear_port'].queryset = RearPort.objects.none()
             self.fields['rear_port'].queryset = RearPort.objects.none()
 
 
 
 
+# class FrontPortBulkCreateForm(
+#     form_from_model(FrontPort, ['type', 'description', 'tags']),
+#     DeviceBulkAddComponentForm
+# ):
+#     tags = TagField(
+#         required=False
+#     )
+
+
 class FrontPortBulkEditForm(BootstrapMixin, AddRemoveTagsForm, BulkEditForm):
 class FrontPortBulkEditForm(BootstrapMixin, AddRemoveTagsForm, BulkEditForm):
     pk = forms.ModelMultipleChoiceField(
     pk = forms.ModelMultipleChoiceField(
         queryset=FrontPort.objects.all(),
         queryset=FrontPort.objects.all(),
@@ -3436,6 +3466,15 @@ class RearPortCSVForm(forms.ModelForm):
         fields = RearPort.csv_headers
         fields = RearPort.csv_headers
 
 
 
 
+# class RearPortBulkCreateForm(
+#     form_from_model(RearPort, ['type', 'positions', 'description', 'tags']),
+#     DeviceBulkAddComponentForm
+# ):
+#     tags = TagField(
+#         required=False
+#     )
+
+
 class RearPortBulkEditForm(BootstrapMixin, AddRemoveTagsForm, BulkEditForm):
 class RearPortBulkEditForm(BootstrapMixin, AddRemoveTagsForm, BulkEditForm):
     pk = forms.ModelMultipleChoiceField(
     pk = forms.ModelMultipleChoiceField(
         queryset=RearPort.objects.all(),
         queryset=RearPort.objects.all(),
@@ -4011,6 +4050,15 @@ class PopulateDeviceBayForm(BootstrapMixin, forms.Form):
         ).exclude(pk=device_bay.device.pk)
         ).exclude(pk=device_bay.device.pk)
 
 
 
 
+class DeviceBayBulkCreateForm(
+    form_from_model(DeviceBay, ['description', 'tags']),
+    DeviceBulkAddComponentForm
+):
+    tags = TagField(
+        required=False
+    )
+
+
 class DeviceBayBulkEditForm(BootstrapMixin, AddRemoveTagsForm, BulkEditForm):
 class DeviceBayBulkEditForm(BootstrapMixin, AddRemoveTagsForm, BulkEditForm):
     pk = forms.ModelMultipleChoiceField(
     pk = forms.ModelMultipleChoiceField(
         queryset=DeviceBay.objects.all(),
         queryset=DeviceBay.objects.all(),

+ 6 - 6
netbox/dcim/views.py

@@ -1930,7 +1930,7 @@ class DeviceBulkAddConsolePortView(PermissionRequiredMixin, BulkComponentCreateV
     permission_required = 'dcim.add_consoleport'
     permission_required = 'dcim.add_consoleport'
     parent_model = Device
     parent_model = Device
     parent_field = 'device'
     parent_field = 'device'
-    form = forms.DeviceBulkAddComponentForm
+    form = forms.ConsolePortBulkCreateForm
     model = ConsolePort
     model = ConsolePort
     model_form = forms.ConsolePortForm
     model_form = forms.ConsolePortForm
     filterset = filters.DeviceFilterSet
     filterset = filters.DeviceFilterSet
@@ -1942,7 +1942,7 @@ class DeviceBulkAddConsoleServerPortView(PermissionRequiredMixin, BulkComponentC
     permission_required = 'dcim.add_consoleserverport'
     permission_required = 'dcim.add_consoleserverport'
     parent_model = Device
     parent_model = Device
     parent_field = 'device'
     parent_field = 'device'
-    form = forms.DeviceBulkAddComponentForm
+    form = forms.ConsoleServerPortBulkCreateForm
     model = ConsoleServerPort
     model = ConsoleServerPort
     model_form = forms.ConsoleServerPortForm
     model_form = forms.ConsoleServerPortForm
     filterset = filters.DeviceFilterSet
     filterset = filters.DeviceFilterSet
@@ -1954,7 +1954,7 @@ class DeviceBulkAddPowerPortView(PermissionRequiredMixin, BulkComponentCreateVie
     permission_required = 'dcim.add_powerport'
     permission_required = 'dcim.add_powerport'
     parent_model = Device
     parent_model = Device
     parent_field = 'device'
     parent_field = 'device'
-    form = forms.DeviceBulkAddComponentForm
+    form = forms.PowerPortBulkCreateForm
     model = PowerPort
     model = PowerPort
     model_form = forms.PowerPortForm
     model_form = forms.PowerPortForm
     filterset = filters.DeviceFilterSet
     filterset = filters.DeviceFilterSet
@@ -1966,7 +1966,7 @@ class DeviceBulkAddPowerOutletView(PermissionRequiredMixin, BulkComponentCreateV
     permission_required = 'dcim.add_poweroutlet'
     permission_required = 'dcim.add_poweroutlet'
     parent_model = Device
     parent_model = Device
     parent_field = 'device'
     parent_field = 'device'
-    form = forms.DeviceBulkAddComponentForm
+    form = forms.PowerOutletBulkCreateForm
     model = PowerOutlet
     model = PowerOutlet
     model_form = forms.PowerOutletForm
     model_form = forms.PowerOutletForm
     filterset = filters.DeviceFilterSet
     filterset = filters.DeviceFilterSet
@@ -1978,7 +1978,7 @@ class DeviceBulkAddInterfaceView(PermissionRequiredMixin, BulkComponentCreateVie
     permission_required = 'dcim.add_interface'
     permission_required = 'dcim.add_interface'
     parent_model = Device
     parent_model = Device
     parent_field = 'device'
     parent_field = 'device'
-    form = forms.DeviceBulkAddInterfaceForm
+    form = forms.InterfaceBulkCreateForm
     model = Interface
     model = Interface
     model_form = forms.InterfaceForm
     model_form = forms.InterfaceForm
     filterset = filters.DeviceFilterSet
     filterset = filters.DeviceFilterSet
@@ -1990,7 +1990,7 @@ class DeviceBulkAddDeviceBayView(PermissionRequiredMixin, BulkComponentCreateVie
     permission_required = 'dcim.add_devicebay'
     permission_required = 'dcim.add_devicebay'
     parent_model = Device
     parent_model = Device
     parent_field = 'device'
     parent_field = 'device'
-    form = forms.DeviceBulkAddComponentForm
+    form = forms.DeviceBayBulkCreateForm
     model = DeviceBay
     model = DeviceBay
     model_form = forms.DeviceBayForm
     model_form = forms.DeviceBayForm
     filterset = filters.DeviceFilterSet
     filterset = filters.DeviceFilterSet

+ 10 - 0
netbox/utilities/forms.py

@@ -10,6 +10,7 @@ from django.conf import settings
 from django.contrib.postgres.forms.jsonb import JSONField as _JSONField, InvalidJSONInput
 from django.contrib.postgres.forms.jsonb import JSONField as _JSONField, InvalidJSONInput
 from django.db.models import Count
 from django.db.models import Count
 from django.forms import BoundField
 from django.forms import BoundField
+from django.forms.models import fields_for_model
 from django.urls import reverse
 from django.urls import reverse
 
 
 from .choices import unpack_grouped_choices
 from .choices import unpack_grouped_choices
@@ -123,6 +124,15 @@ def add_blank_choice(choices):
     return ((None, '---------'),) + tuple(choices)
     return ((None, '---------'),) + tuple(choices)
 
 
 
 
+def form_from_model(model, fields):
+    """
+    Return a Form class with the specified fields from a model.
+    """
+    form_fields = fields_for_model(model, fields=fields)
+
+    return type('FormFromModel', (forms.Form,), form_fields)
+
+
 #
 #
 # Widgets
 # Widgets
 #
 #