mirror of
https://github.com/netbox-community/netbox.git
synced 2025-07-24 09:28:38 -06:00
Introduce model-specific bulk create forms for device components
This commit is contained in:
parent
131d2c97ca
commit
97b8e73716
@ -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,
|
DynamicModelMultipleChoiceField, ExpandableNameField, FlexibleModelChoiceField, form_from_model, JSONField,
|
||||||
SmallTextarea, SlugField, StaticSelect2, StaticSelect2Multiple, TagFilterField, BOOLEAN_WITH_BLANK_CHOICES,
|
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,71 +2997,13 @@ 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):
|
class InterfaceBulkCreateForm(
|
||||||
device = FlexibleModelChoiceField(
|
form_from_model(Interface, ['type', 'enabled', 'mtu', 'mgmt_only', 'description', 'tags']),
|
||||||
queryset=Device.objects.all(),
|
DeviceBulkAddComponentForm
|
||||||
required=False,
|
):
|
||||||
to_field_name='name',
|
tags = TagField(
|
||||||
help_text='Name or ID of device',
|
required=False
|
||||||
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']
|
|
||||||
|
|
||||||
|
|
||||||
class InterfaceBulkEditForm(BootstrapMixin, AddRemoveTagsForm, BulkEditForm):
|
class InterfaceBulkEditForm(BootstrapMixin, AddRemoveTagsForm, BulkEditForm):
|
||||||
@ -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(),
|
||||||
|
@ -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,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
|
||||||
#
|
#
|
||||||
|
Loading…
Reference in New Issue
Block a user