From f805b577788ca3ca3f3f4d4905802812615e416e Mon Sep 17 00:00:00 2001 From: Jeremy Stretch Date: Tue, 4 Feb 2020 18:08:40 -0500 Subject: [PATCH] Adapt BulkEditView to not require a parent object for device components --- netbox/dcim/forms.py | 12 ++++++++---- netbox/dcim/urls.py | 13 ++++++++----- netbox/dcim/views.py | 5 ----- netbox/templates/dcim/device.html | 10 +++++----- .../templates/virtualization/virtualmachine.html | 2 +- netbox/utilities/forms.py | 3 +-- netbox/utilities/views.py | 14 +++----------- netbox/virtualization/forms.py | 6 ++++-- netbox/virtualization/urls.py | 2 +- 9 files changed, 31 insertions(+), 36 deletions(-) diff --git a/netbox/dcim/forms.py b/netbox/dcim/forms.py index 723491610..14e13ad44 100644 --- a/netbox/dcim/forms.py +++ b/netbox/dcim/forms.py @@ -2525,7 +2525,11 @@ class PowerOutletBulkEditForm(BootstrapMixin, AddRemoveTagsForm, BulkEditForm): super().__init__(*args, **kwargs) # Limit power_port queryset to PowerPorts which belong to the parent Device - self.fields['power_port'].queryset = PowerPort.objects.filter(device=self.parent_obj) + if 'device' in self.initial: + device = Device.objects.filter(pk=self.initial['device']).first() + self.fields['power_port'].queryset = PowerPort.objects.filter(device=device) + else: + self.fields['power_port'].queryset = PowerPort.objects.none() class PowerOutletBulkRenameForm(BulkRenameForm): @@ -2836,14 +2840,14 @@ class InterfaceBulkEditForm(BootstrapMixin, AddRemoveTagsForm, BulkEditForm): super().__init__(*args, **kwargs) # Limit LAG choices to interfaces which belong to the parent device (or VC master) - device = self.parent_obj - if device is not None: + if 'device' in self.initial: + device = Device.objects.filter(pk=self.initial['device']).first() self.fields['lag'].queryset = Interface.objects.filter( device__in=[device, device.get_vc_master()], type=InterfaceTypeChoices.TYPE_LAG ) else: - self.fields['lag'].choices = [] + self.fields['lag'].queryset = Interface.objects.none() def clean(self): diff --git a/netbox/dcim/urls.py b/netbox/dcim/urls.py index fd2f165d4..c6057b4a3 100644 --- a/netbox/dcim/urls.py +++ b/netbox/dcim/urls.py @@ -172,6 +172,7 @@ urlpatterns = [ path(r'devices//console-ports/add/', views.ConsolePortCreateView.as_view(), name='consoleport_add'), path(r'console-ports/', views.ConsolePortListView.as_view(), name='consoleport_list'), path(r'console-ports/import/', views.ConsolePortBulkImportView.as_view(), name='consoleport_import'), + # TODO: Bulk edit view for ConsolePorts path(r'console-ports/delete/', views.ConsolePortBulkDeleteView.as_view(), name='consoleport_bulk_delete'), path(r'console-ports//connect//', views.CableCreateView.as_view(), name='consoleport_connect', kwargs={'termination_a_type': ConsolePort}), path(r'console-ports//edit/', views.ConsolePortEditView.as_view(), name='consoleport_edit'), @@ -181,11 +182,11 @@ urlpatterns = [ # Console server ports path(r'devices/console-server-ports/add/', views.DeviceBulkAddConsoleServerPortView.as_view(), name='device_bulk_add_consoleserverport'), path(r'devices//console-server-ports/add/', views.ConsoleServerPortCreateView.as_view(), name='consoleserverport_add'), - path(r'devices//console-server-ports/edit/', views.ConsoleServerPortBulkEditView.as_view(), name='consoleserverport_bulk_edit'), path(r'console-server-ports/', views.ConsoleServerPortListView.as_view(), name='consoleserverport_list'), path(r'console-server-ports/rename/', views.ConsoleServerPortBulkRenameView.as_view(), name='consoleserverport_bulk_rename'), path(r'console-server-ports/disconnect/', views.ConsoleServerPortBulkDisconnectView.as_view(), name='consoleserverport_bulk_disconnect'), path(r'console-server-ports/import/', views.ConsoleServerPortBulkImportView.as_view(), name='consoleserverport_import'), + path(r'console-server-ports/edit/', views.ConsoleServerPortBulkEditView.as_view(), name='consoleserverport_bulk_edit'), path(r'console-server-ports/delete/', views.ConsoleServerPortBulkDeleteView.as_view(), name='consoleserverport_bulk_delete'), path(r'console-server-ports//connect//', views.CableCreateView.as_view(), name='consoleserverport_connect', kwargs={'termination_a_type': ConsoleServerPort}), path(r'console-server-ports//edit/', views.ConsoleServerPortEditView.as_view(), name='consoleserverport_edit'), @@ -197,6 +198,7 @@ urlpatterns = [ path(r'devices//power-ports/add/', views.PowerPortCreateView.as_view(), name='powerport_add'), path(r'power-ports/', views.PowerPortListView.as_view(), name='powerport_list'), path(r'power-ports/import/', views.PowerPortBulkImportView.as_view(), name='powerport_import'), + # TODO: Bulk edit view for PowerPorts path(r'power-ports/delete/', views.PowerPortBulkDeleteView.as_view(), name='powerport_bulk_delete'), path(r'power-ports//connect//', views.CableCreateView.as_view(), name='powerport_connect', kwargs={'termination_a_type': PowerPort}), path(r'power-ports//edit/', views.PowerPortEditView.as_view(), name='powerport_edit'), @@ -206,11 +208,11 @@ urlpatterns = [ # Power outlets path(r'devices/power-outlets/add/', views.DeviceBulkAddPowerOutletView.as_view(), name='device_bulk_add_poweroutlet'), path(r'devices//power-outlets/add/', views.PowerOutletCreateView.as_view(), name='poweroutlet_add'), - path(r'devices//power-outlets/edit/', views.PowerOutletBulkEditView.as_view(), name='poweroutlet_bulk_edit'), path(r'power-outlets/', views.PowerOutletListView.as_view(), name='poweroutlet_list'), path(r'power-outlets/rename/', views.PowerOutletBulkRenameView.as_view(), name='poweroutlet_bulk_rename'), path(r'power-outlets/disconnect/', views.PowerOutletBulkDisconnectView.as_view(), name='poweroutlet_bulk_disconnect'), path(r'power-outlets/import/', views.PowerOutletBulkImportView.as_view(), name='poweroutlet_import'), + path(r'power-outlets/edit/', views.PowerOutletBulkEditView.as_view(), name='poweroutlet_bulk_edit'), path(r'power-outlets/delete/', views.PowerOutletBulkDeleteView.as_view(), name='poweroutlet_bulk_delete'), path(r'power-outlets//connect//', views.CableCreateView.as_view(), name='poweroutlet_connect', kwargs={'termination_a_type': PowerOutlet}), path(r'power-outlets//edit/', views.PowerOutletEditView.as_view(), name='poweroutlet_edit'), @@ -220,11 +222,11 @@ urlpatterns = [ # Interfaces path(r'devices/interfaces/add/', views.DeviceBulkAddInterfaceView.as_view(), name='device_bulk_add_interface'), path(r'devices//interfaces/add/', views.InterfaceCreateView.as_view(), name='interface_add'), - path(r'devices//interfaces/edit/', views.InterfaceBulkEditView.as_view(), name='interface_bulk_edit'), path(r'interfaces/', views.InterfaceListView.as_view(), name='interface_list'), path(r'interfaces/rename/', views.InterfaceBulkRenameView.as_view(), name='interface_bulk_rename'), path(r'interfaces/disconnect/', views.InterfaceBulkDisconnectView.as_view(), name='interface_bulk_disconnect'), path(r'interfaces/import/', views.InterfaceBulkImportView.as_view(), name='interface_import'), + path(r'interfaces/edit/', views.InterfaceBulkEditView.as_view(), name='interface_bulk_edit'), path(r'interfaces/delete/', views.InterfaceBulkDeleteView.as_view(), name='interface_bulk_delete'), path(r'interfaces//connect//', views.CableCreateView.as_view(), name='interface_connect', kwargs={'termination_a_type': Interface}), path(r'interfaces//', views.InterfaceView.as_view(), name='interface'), @@ -236,11 +238,11 @@ urlpatterns = [ # Front ports # path(r'devices/front-ports/add/', views.DeviceBulkAddFrontPortView.as_view(), name='device_bulk_add_frontport'), path(r'devices//front-ports/add/', views.FrontPortCreateView.as_view(), name='frontport_add'), - path(r'devices//front-ports/edit/', views.FrontPortBulkEditView.as_view(), name='frontport_bulk_edit'), path(r'front-ports/', views.FrontPortListView.as_view(), name='frontport_list'), path(r'front-ports/rename/', views.FrontPortBulkRenameView.as_view(), name='frontport_bulk_rename'), path(r'front-ports/disconnect/', views.FrontPortBulkDisconnectView.as_view(), name='frontport_bulk_disconnect'), path(r'front-ports/import/', views.FrontPortBulkImportView.as_view(), name='frontport_import'), + path(r'front-ports/edit/', views.FrontPortBulkEditView.as_view(), name='frontport_bulk_edit'), path(r'front-ports/delete/', views.FrontPortBulkDeleteView.as_view(), name='frontport_bulk_delete'), path(r'front-ports//connect//', views.CableCreateView.as_view(), name='frontport_connect', kwargs={'termination_a_type': FrontPort}), path(r'front-ports//edit/', views.FrontPortEditView.as_view(), name='frontport_edit'), @@ -250,11 +252,11 @@ urlpatterns = [ # Rear ports # path(r'devices/rear-ports/add/', views.DeviceBulkAddRearPortView.as_view(), name='device_bulk_add_rearport'), path(r'devices//rear-ports/add/', views.RearPortCreateView.as_view(), name='rearport_add'), - path(r'devices//rear-ports/edit/', views.RearPortBulkEditView.as_view(), name='rearport_bulk_edit'), path(r'rear-ports/', views.RearPortListView.as_view(), name='rearport_list'), path(r'rear-ports/rename/', views.RearPortBulkRenameView.as_view(), name='rearport_bulk_rename'), path(r'rear-ports/disconnect/', views.RearPortBulkDisconnectView.as_view(), name='rearport_bulk_disconnect'), path(r'rear-ports/import/', views.RearPortBulkImportView.as_view(), name='rearport_import'), + path(r'rear-ports/edit/', views.RearPortBulkEditView.as_view(), name='rearport_bulk_edit'), path(r'rear-ports/delete/', views.RearPortBulkDeleteView.as_view(), name='rearport_bulk_delete'), path(r'rear-ports//connect//', views.CableCreateView.as_view(), name='rearport_connect', kwargs={'termination_a_type': RearPort}), path(r'rear-ports//edit/', views.RearPortEditView.as_view(), name='rearport_edit'), @@ -267,6 +269,7 @@ urlpatterns = [ path(r'device-bays/', views.DeviceBayListView.as_view(), name='devicebay_list'), path(r'device-bays/rename/', views.DeviceBayBulkRenameView.as_view(), name='devicebay_bulk_rename'), path(r'device-bays/import/', views.DeviceBayBulkImportView.as_view(), name='devicebay_import'), + # TODO: Bulk edit view for DeviceBays path(r'device-bays/delete/', views.DeviceBayBulkDeleteView.as_view(), name='devicebay_bulk_delete'), path(r'device-bays//edit/', views.DeviceBayEditView.as_view(), name='devicebay_edit'), path(r'device-bays//delete/', views.DeviceBayDeleteView.as_view(), name='devicebay_delete'), diff --git a/netbox/dcim/views.py b/netbox/dcim/views.py index 2fdf7f457..9d9223e29 100644 --- a/netbox/dcim/views.py +++ b/netbox/dcim/views.py @@ -1282,7 +1282,6 @@ class ConsoleServerPortBulkImportView(PermissionRequiredMixin, BulkImportView): class ConsoleServerPortBulkEditView(PermissionRequiredMixin, BulkEditView): permission_required = 'dcim.change_consoleserverport' queryset = ConsoleServerPort.objects.all() - parent_model = Device table = tables.ConsoleServerPortTable form = forms.ConsoleServerPortBulkEditForm @@ -1398,7 +1397,6 @@ class PowerOutletBulkImportView(PermissionRequiredMixin, BulkImportView): class PowerOutletBulkEditView(PermissionRequiredMixin, BulkEditView): permission_required = 'dcim.change_poweroutlet' queryset = PowerOutlet.objects.all() - parent_model = Device table = tables.PowerOutletTable form = forms.PowerOutletBulkEditForm @@ -1503,7 +1501,6 @@ class InterfaceBulkImportView(PermissionRequiredMixin, BulkImportView): class InterfaceBulkEditView(PermissionRequiredMixin, BulkEditView): permission_required = 'dcim.change_interface' queryset = Interface.objects.all() - parent_model = Device table = tables.InterfaceTable form = forms.InterfaceBulkEditForm @@ -1571,7 +1568,6 @@ class FrontPortBulkImportView(PermissionRequiredMixin, BulkImportView): class FrontPortBulkEditView(PermissionRequiredMixin, BulkEditView): permission_required = 'dcim.change_frontport' queryset = FrontPort.objects.all() - parent_model = Device table = tables.FrontPortTable form = forms.FrontPortBulkEditForm @@ -1639,7 +1635,6 @@ class RearPortBulkImportView(PermissionRequiredMixin, BulkImportView): class RearPortBulkEditView(PermissionRequiredMixin, BulkEditView): permission_required = 'dcim.change_rearport' queryset = RearPort.objects.all() - parent_model = Device table = tables.RearPortTable form = forms.RearPortBulkEditForm diff --git a/netbox/templates/dcim/device.html b/netbox/templates/dcim/device.html index 57c068727..089483354 100644 --- a/netbox/templates/dcim/device.html +++ b/netbox/templates/dcim/device.html @@ -587,7 +587,7 @@ - {% endif %} @@ -649,7 +649,7 @@ - - - - - {% endif %} diff --git a/netbox/utilities/forms.py b/netbox/utilities/forms.py index a6eca7382..77c3dd18c 100644 --- a/netbox/utilities/forms.py +++ b/netbox/utilities/forms.py @@ -743,10 +743,9 @@ class BulkEditForm(forms.Form): """ Base form for editing multiple objects in bulk """ - def __init__(self, model, parent_obj=None, *args, **kwargs): + def __init__(self, model, *args, **kwargs): super().__init__(*args, **kwargs) self.model = model - self.parent_obj = parent_obj self.nullable_fields = [] # Copy any nullable fields defined in Meta diff --git a/netbox/utilities/views.py b/netbox/utilities/views.py index f32c57c3f..bea9450bd 100644 --- a/netbox/utilities/views.py +++ b/netbox/utilities/views.py @@ -604,14 +604,12 @@ class BulkEditView(GetReturnURLMixin, View): Edit objects in bulk. queryset: Custom queryset to use when retrieving objects (e.g. to select related objects) - parent_model: The model of the parent object (if any) filter: FilterSet to apply when deleting by QuerySet table: The table used to display devices being edited form: The form class used to edit objects in bulk template_name: The name of the template """ queryset = None - parent_model = None filterset = None table = None form = None @@ -624,12 +622,6 @@ class BulkEditView(GetReturnURLMixin, View): model = self.queryset.model - # Attempt to derive parent object if a parent class has been given - if self.parent_model: - parent_obj = get_object_or_404(self.parent_model, **kwargs) - else: - parent_obj = None - # Are we editing *all* objects in the queryset or just a selected subset? if request.POST.get('_all') and self.filterset is not None: pk_list = [obj.pk for obj in self.filterset(request.GET, model.objects.only('pk')).qs] @@ -637,7 +629,7 @@ class BulkEditView(GetReturnURLMixin, View): pk_list = [int(pk) for pk in request.POST.getlist('pk')] if '_apply' in request.POST: - form = self.form(model, parent_obj, request.POST) + form = self.form(model, request.POST, initial=request.GET) if form.is_valid(): custom_fields = form.custom_fields if hasattr(form, 'custom_fields') else [] @@ -719,9 +711,9 @@ class BulkEditView(GetReturnURLMixin, View): messages.error(self.request, "{} failed validation: {}".format(obj, e)) else: - initial_data = request.POST.copy() + initial_data = request.GET.copy() initial_data['pk'] = pk_list - form = self.form(model, parent_obj, initial=initial_data) + form = self.form(model, initial=initial_data) # Retrieve objects being edited table = self.table(self.queryset.filter(pk__in=pk_list), orderable=False) diff --git a/netbox/virtualization/forms.py b/netbox/virtualization/forms.py index b470bace2..f5ecad119 100644 --- a/netbox/virtualization/forms.py +++ b/netbox/virtualization/forms.py @@ -881,6 +881,8 @@ class InterfaceBulkEditForm(BootstrapMixin, BulkEditForm): def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) + parent_obj = VirtualMachine.objects.filter(pk=self.initial.get('virtual_machine')).first() + # Limit VLan choices to those in: global vlans, global groups, the current site's group, the current site vlan_choices = [] global_vlans = VLAN.objects.filter(site=None, group=None) @@ -892,8 +894,8 @@ class InterfaceBulkEditForm(BootstrapMixin, BulkEditForm): vlan_choices.append( (group.name, [(vlan.pk, vlan) for vlan in global_group_vlans]) ) - if self.parent_obj.cluster is not None: - site = getattr(self.parent_obj.cluster, 'site', None) + if parent_obj.cluster is not None: + site = getattr(parent_obj.cluster, 'site', None) if site is not None: # Add non-grouped site VLANs diff --git a/netbox/virtualization/urls.py b/netbox/virtualization/urls.py index 88a8dc6d9..587ec2c34 100644 --- a/netbox/virtualization/urls.py +++ b/netbox/virtualization/urls.py @@ -53,7 +53,7 @@ urlpatterns = [ # VM interfaces path(r'virtual-machines/interfaces/add/', views.VirtualMachineBulkAddInterfaceView.as_view(), name='virtualmachine_bulk_add_interface'), path(r'virtual-machines//interfaces/add/', views.InterfaceCreateView.as_view(), name='interface_add'), - path(r'virtual-machines//interfaces/edit/', views.InterfaceBulkEditView.as_view(), name='interface_bulk_edit'), + path(r'interfaces/edit/', views.InterfaceBulkEditView.as_view(), name='interface_bulk_edit'), path(r'interfaces/delete/', views.InterfaceBulkDeleteView.as_view(), name='interface_bulk_delete'), # TODO: Rename vm-interfaces to interfaces path(r'vm-interfaces//edit/', views.InterfaceEditView.as_view(), name='interface_edit'),