mirror of
https://github.com/netbox-community/netbox.git
synced 2025-07-22 12:06:53 -06:00
Merge pull request #4117 from netbox-community/4116-component-bulk-actions
Closes #4116: Enable bulk edit and delete functions for device component list views
This commit is contained in:
commit
1e61fcb485
@ -3,6 +3,7 @@
|
||||
## Enhancements
|
||||
|
||||
* [#4113](https://github.com/netbox-community/netbox/issues/4113) - Add bulk edit functionality for device type components
|
||||
* [#4116](https://github.com/netbox-community/netbox/issues/4116) - Enable bulk edit and delete functions for device component list views
|
||||
|
||||
## Bug Fixes
|
||||
|
||||
|
@ -2371,6 +2371,27 @@ class ConsolePortCreateForm(BootstrapMixin, forms.Form):
|
||||
)
|
||||
|
||||
|
||||
class ConsolePortBulkEditForm(BootstrapMixin, AddRemoveTagsForm, BulkEditForm):
|
||||
pk = forms.ModelMultipleChoiceField(
|
||||
queryset=ConsolePort.objects.all(),
|
||||
widget=forms.MultipleHiddenInput()
|
||||
)
|
||||
type = forms.ChoiceField(
|
||||
choices=add_blank_choice(ConsolePortTypeChoices),
|
||||
required=False,
|
||||
widget=StaticSelect2()
|
||||
)
|
||||
description = forms.CharField(
|
||||
max_length=100,
|
||||
required=False
|
||||
)
|
||||
|
||||
class Meta:
|
||||
nullable_fields = (
|
||||
'description',
|
||||
)
|
||||
|
||||
|
||||
class ConsolePortCSVForm(forms.ModelForm):
|
||||
device = FlexibleModelChoiceField(
|
||||
queryset=Device.objects.all(),
|
||||
@ -2544,6 +2565,37 @@ class PowerPortCreateForm(BootstrapMixin, forms.Form):
|
||||
)
|
||||
|
||||
|
||||
class PowerPortBulkEditForm(BootstrapMixin, AddRemoveTagsForm, BulkEditForm):
|
||||
pk = forms.ModelMultipleChoiceField(
|
||||
queryset=PowerPort.objects.all(),
|
||||
widget=forms.MultipleHiddenInput()
|
||||
)
|
||||
type = forms.ChoiceField(
|
||||
choices=add_blank_choice(PowerPortTypeChoices),
|
||||
required=False,
|
||||
widget=StaticSelect2()
|
||||
)
|
||||
maximum_draw = forms.IntegerField(
|
||||
min_value=1,
|
||||
required=False,
|
||||
help_text="Maximum draw in watts"
|
||||
)
|
||||
allocated_draw = forms.IntegerField(
|
||||
min_value=1,
|
||||
required=False,
|
||||
help_text="Allocated draw in watts"
|
||||
)
|
||||
description = forms.CharField(
|
||||
max_length=100,
|
||||
required=False
|
||||
)
|
||||
|
||||
class Meta:
|
||||
nullable_fields = (
|
||||
'description',
|
||||
)
|
||||
|
||||
|
||||
class PowerPortCSVForm(forms.ModelForm):
|
||||
device = FlexibleModelChoiceField(
|
||||
queryset=Device.objects.all(),
|
||||
@ -2695,6 +2747,7 @@ class PowerOutletBulkEditForm(BootstrapMixin, AddRemoveTagsForm, BulkEditForm):
|
||||
)
|
||||
device = forms.ModelChoiceField(
|
||||
queryset=Device.objects.all(),
|
||||
required=False,
|
||||
widget=forms.HiddenInput()
|
||||
)
|
||||
type = forms.ChoiceField(
|
||||
@ -2726,6 +2779,9 @@ class PowerOutletBulkEditForm(BootstrapMixin, AddRemoveTagsForm, BulkEditForm):
|
||||
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'].choices = ()
|
||||
self.fields['power_port'].widget.attrs['disabled'] = True
|
||||
|
||||
|
||||
class PowerOutletBulkRenameForm(BulkRenameForm):
|
||||
@ -2969,6 +3025,7 @@ class InterfaceBulkEditForm(BootstrapMixin, AddRemoveTagsForm, BulkEditForm):
|
||||
)
|
||||
device = forms.ModelChoiceField(
|
||||
queryset=Device.objects.all(),
|
||||
required=False,
|
||||
widget=forms.HiddenInput()
|
||||
)
|
||||
type = forms.ChoiceField(
|
||||
@ -3044,6 +3101,9 @@ class InterfaceBulkEditForm(BootstrapMixin, AddRemoveTagsForm, BulkEditForm):
|
||||
device__in=[device, device.get_vc_master()],
|
||||
type=InterfaceTypeChoices.TYPE_LAG
|
||||
)
|
||||
else:
|
||||
self.fields['lag'].choices = ()
|
||||
self.fields['lag'].widget.attrs['disabled'] = True
|
||||
|
||||
def clean(self):
|
||||
|
||||
|
@ -1070,7 +1070,6 @@ class ConsolePortTestCase(StandardTestCases.Views):
|
||||
# Disable inapplicable views
|
||||
test_get_object = None
|
||||
test_create_object = None
|
||||
test_bulk_edit_objects = None
|
||||
|
||||
def test_bulk_create_objects(self):
|
||||
return self._test_bulk_create_objects(expected_count=3)
|
||||
@ -1101,6 +1100,11 @@ class ConsolePortTestCase(StandardTestCases.Views):
|
||||
'tags': 'Alpha,Bravo,Charlie',
|
||||
}
|
||||
|
||||
cls.bulk_edit_data = {
|
||||
'type': ConsolePortTypeChoices.TYPE_RJ45,
|
||||
'description': 'New description',
|
||||
}
|
||||
|
||||
cls.csv_data = (
|
||||
"device,name",
|
||||
"Device 1,Console Port 4",
|
||||
@ -1164,7 +1168,6 @@ class PowerPortTestCase(StandardTestCases.Views):
|
||||
|
||||
# Disable inapplicable views
|
||||
test_get_object = None
|
||||
test_bulk_edit_objects = None
|
||||
test_create_object = None
|
||||
|
||||
def test_bulk_create_objects(self):
|
||||
@ -1200,6 +1203,13 @@ class PowerPortTestCase(StandardTestCases.Views):
|
||||
'tags': 'Alpha,Bravo,Charlie',
|
||||
}
|
||||
|
||||
cls.bulk_edit_data = {
|
||||
'type': PowerPortTypeChoices.TYPE_IEC_C14,
|
||||
'maximum_draw': 100,
|
||||
'allocated_draw': 50,
|
||||
'description': 'New description',
|
||||
}
|
||||
|
||||
cls.csv_data = (
|
||||
"device,name",
|
||||
"Device 1,Power Port 4",
|
||||
|
@ -178,7 +178,8 @@ urlpatterns = [
|
||||
path('console-ports/', views.ConsolePortListView.as_view(), name='consoleport_list'),
|
||||
path('console-ports/add/', views.ConsolePortCreateView.as_view(), name='consoleport_add'),
|
||||
path('console-ports/import/', views.ConsolePortBulkImportView.as_view(), name='consoleport_import'),
|
||||
# TODO: Bulk edit, rename, disconnect views for ConsolePorts
|
||||
path('console-ports/edit/', views.ConsolePortBulkEditView.as_view(), name='consoleport_bulk_edit'),
|
||||
# TODO: Bulk rename, disconnect views for ConsolePorts
|
||||
path('console-ports/delete/', views.ConsolePortBulkDeleteView.as_view(), name='consoleport_bulk_delete'),
|
||||
path('console-ports/<int:termination_a_id>/connect/<str:termination_b_type>/', views.CableCreateView.as_view(), name='consoleport_connect', kwargs={'termination_a_type': ConsolePort}),
|
||||
path('console-ports/<int:pk>/edit/', views.ConsolePortEditView.as_view(), name='consoleport_edit'),
|
||||
@ -204,7 +205,8 @@ urlpatterns = [
|
||||
path('power-ports/', views.PowerPortListView.as_view(), name='powerport_list'),
|
||||
path('power-ports/add/', views.PowerPortCreateView.as_view(), name='powerport_add'),
|
||||
path('power-ports/import/', views.PowerPortBulkImportView.as_view(), name='powerport_import'),
|
||||
# TODO: Bulk edit, rename, disconnect views for PowerPorts
|
||||
path('power-ports/edit/', views.PowerPortBulkEditView.as_view(), name='powerport_bulk_edit'),
|
||||
# TODO: Bulk rename, disconnect views for PowerPorts
|
||||
path('power-ports/delete/', views.PowerPortBulkDeleteView.as_view(), name='powerport_bulk_delete'),
|
||||
path('power-ports/<int:termination_a_id>/connect/<str:termination_b_type>/', views.CableCreateView.as_view(), name='powerport_connect', kwargs={'termination_a_type': PowerPort}),
|
||||
path('power-ports/<int:pk>/edit/', views.PowerPortEditView.as_view(), name='powerport_edit'),
|
||||
|
@ -1224,7 +1224,7 @@ class ConsolePortListView(PermissionRequiredMixin, ObjectListView):
|
||||
filterset = filters.ConsolePortFilterSet
|
||||
filterset_form = forms.ConsolePortFilterForm
|
||||
table = tables.ConsolePortDetailTable
|
||||
template_name = 'dcim/device_component_list.html'
|
||||
template_name = 'dcim/consoleport_list.html'
|
||||
|
||||
|
||||
class ConsolePortCreateView(PermissionRequiredMixin, ComponentCreateView):
|
||||
@ -1253,6 +1253,13 @@ class ConsolePortBulkImportView(PermissionRequiredMixin, BulkImportView):
|
||||
default_return_url = 'dcim:consoleport_list'
|
||||
|
||||
|
||||
class ConsolePortBulkEditView(PermissionRequiredMixin, BulkEditView):
|
||||
permission_required = 'dcim.change_consoleport'
|
||||
queryset = ConsolePort.objects.all()
|
||||
table = tables.ConsolePortTable
|
||||
form = forms.ConsolePortBulkEditForm
|
||||
|
||||
|
||||
class ConsolePortBulkDeleteView(PermissionRequiredMixin, BulkDeleteView):
|
||||
permission_required = 'dcim.delete_consoleport'
|
||||
queryset = ConsolePort.objects.all()
|
||||
@ -1270,7 +1277,7 @@ class ConsoleServerPortListView(PermissionRequiredMixin, ObjectListView):
|
||||
filterset = filters.ConsoleServerPortFilterSet
|
||||
filterset_form = forms.ConsoleServerPortFilterForm
|
||||
table = tables.ConsoleServerPortDetailTable
|
||||
template_name = 'dcim/device_component_list.html'
|
||||
template_name = 'dcim/consoleserverport_list.html'
|
||||
|
||||
|
||||
class ConsoleServerPortCreateView(PermissionRequiredMixin, ComponentCreateView):
|
||||
@ -1335,7 +1342,7 @@ class PowerPortListView(PermissionRequiredMixin, ObjectListView):
|
||||
filterset = filters.PowerPortFilterSet
|
||||
filterset_form = forms.PowerPortFilterForm
|
||||
table = tables.PowerPortDetailTable
|
||||
template_name = 'dcim/device_component_list.html'
|
||||
template_name = 'dcim/powerport_list.html'
|
||||
|
||||
|
||||
class PowerPortCreateView(PermissionRequiredMixin, ComponentCreateView):
|
||||
@ -1364,6 +1371,13 @@ class PowerPortBulkImportView(PermissionRequiredMixin, BulkImportView):
|
||||
default_return_url = 'dcim:powerport_list'
|
||||
|
||||
|
||||
class PowerPortBulkEditView(PermissionRequiredMixin, BulkEditView):
|
||||
permission_required = 'dcim.change_powerport'
|
||||
queryset = PowerPort.objects.all()
|
||||
table = tables.PowerPortTable
|
||||
form = forms.PowerPortBulkEditForm
|
||||
|
||||
|
||||
class PowerPortBulkDeleteView(PermissionRequiredMixin, BulkDeleteView):
|
||||
permission_required = 'dcim.delete_powerport'
|
||||
queryset = PowerPort.objects.all()
|
||||
@ -1381,7 +1395,7 @@ class PowerOutletListView(PermissionRequiredMixin, ObjectListView):
|
||||
filterset = filters.PowerOutletFilterSet
|
||||
filterset_form = forms.PowerOutletFilterForm
|
||||
table = tables.PowerOutletDetailTable
|
||||
template_name = 'dcim/device_component_list.html'
|
||||
template_name = 'dcim/poweroutlet_list.html'
|
||||
|
||||
|
||||
class PowerOutletCreateView(PermissionRequiredMixin, ComponentCreateView):
|
||||
@ -1446,7 +1460,7 @@ class InterfaceListView(PermissionRequiredMixin, ObjectListView):
|
||||
filterset = filters.InterfaceFilterSet
|
||||
filterset_form = forms.InterfaceFilterForm
|
||||
table = tables.InterfaceDetailTable
|
||||
template_name = 'dcim/device_component_list.html'
|
||||
template_name = 'dcim/interface_list.html'
|
||||
|
||||
|
||||
class InterfaceView(PermissionRequiredMixin, View):
|
||||
@ -1548,7 +1562,7 @@ class FrontPortListView(PermissionRequiredMixin, ObjectListView):
|
||||
filterset = filters.FrontPortFilterSet
|
||||
filterset_form = forms.FrontPortFilterForm
|
||||
table = tables.FrontPortDetailTable
|
||||
template_name = 'dcim/device_component_list.html'
|
||||
template_name = 'dcim/frontport_list.html'
|
||||
|
||||
|
||||
class FrontPortCreateView(PermissionRequiredMixin, ComponentCreateView):
|
||||
@ -1613,7 +1627,7 @@ class RearPortListView(PermissionRequiredMixin, ObjectListView):
|
||||
filterset = filters.RearPortFilterSet
|
||||
filterset_form = forms.RearPortFilterForm
|
||||
table = tables.RearPortDetailTable
|
||||
template_name = 'dcim/device_component_list.html'
|
||||
template_name = 'dcim/rearport_list.html'
|
||||
|
||||
|
||||
class RearPortCreateView(PermissionRequiredMixin, ComponentCreateView):
|
||||
@ -1680,7 +1694,7 @@ class DeviceBayListView(PermissionRequiredMixin, ObjectListView):
|
||||
filterset = filters.DeviceBayFilterSet
|
||||
filterset_form = forms.DeviceBayFilterForm
|
||||
table = tables.DeviceBayDetailTable
|
||||
template_name = 'dcim/device_component_list.html'
|
||||
template_name = 'dcim/devicebay_list.html'
|
||||
|
||||
|
||||
class DeviceBayCreateView(PermissionRequiredMixin, ComponentCreateView):
|
||||
|
@ -1,16 +1,14 @@
|
||||
{% extends '_base.html' %}
|
||||
{% load buttons %}
|
||||
{% load helpers %}
|
||||
|
||||
{% block content %}
|
||||
<div class="pull-right noprint">
|
||||
{% export_button content_type %}
|
||||
</div>
|
||||
<h1>{% block title %}{{ table.Meta.model|model_name|capfirst }}s{% endblock %}</h1>
|
||||
<h1>{% block title %}Console Ports{% endblock %}</h1>
|
||||
<div class="row">
|
||||
<div class="col-md-9">
|
||||
{% include 'responsive_table.html' %}
|
||||
{% include 'inc/paginator.html' with paginator=table.paginator page=table.page %}
|
||||
{% include 'utilities/obj_table.html' with bulk_edit_url='dcim:consoleport_bulk_edit' bulk_delete_url='dcim:consoleport_bulk_delete' %}
|
||||
</div>
|
||||
<div class="col-md-3 noprint">
|
||||
{% include 'inc/search_panel.html' %}
|
17
netbox/templates/dcim/consoleserverport_list.html
Normal file
17
netbox/templates/dcim/consoleserverport_list.html
Normal file
@ -0,0 +1,17 @@
|
||||
{% extends '_base.html' %}
|
||||
{% load buttons %}
|
||||
|
||||
{% block content %}
|
||||
<div class="pull-right noprint">
|
||||
{% export_button content_type %}
|
||||
</div>
|
||||
<h1>{% block title %}Console Server Ports{% endblock %}</h1>
|
||||
<div class="row">
|
||||
<div class="col-md-9">
|
||||
{% include 'utilities/obj_table.html' with bulk_edit_url='dcim:consoleserverport_bulk_edit' bulk_delete_url='dcim:consoleserverport_bulk_delete' %}
|
||||
</div>
|
||||
<div class="col-md-3 noprint">
|
||||
{% include 'inc/search_panel.html' %}
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
17
netbox/templates/dcim/devicebay_list.html
Normal file
17
netbox/templates/dcim/devicebay_list.html
Normal file
@ -0,0 +1,17 @@
|
||||
{% extends '_base.html' %}
|
||||
{% load buttons %}
|
||||
|
||||
{% block content %}
|
||||
<div class="pull-right noprint">
|
||||
{% export_button content_type %}
|
||||
</div>
|
||||
<h1>{% block title %}Device Bays{% endblock %}</h1>
|
||||
<div class="row">
|
||||
<div class="col-md-9">
|
||||
{% include 'utilities/obj_table.html' with bulk_delete_url='dcim:devicebay_bulk_delete' %}
|
||||
</div>
|
||||
<div class="col-md-3 noprint">
|
||||
{% include 'inc/search_panel.html' %}
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
17
netbox/templates/dcim/frontport_list.html
Normal file
17
netbox/templates/dcim/frontport_list.html
Normal file
@ -0,0 +1,17 @@
|
||||
{% extends '_base.html' %}
|
||||
{% load buttons %}
|
||||
|
||||
{% block content %}
|
||||
<div class="pull-right noprint">
|
||||
{% export_button content_type %}
|
||||
</div>
|
||||
<h1>{% block title %}Front Ports{% endblock %}</h1>
|
||||
<div class="row">
|
||||
<div class="col-md-9">
|
||||
{% include 'utilities/obj_table.html' with bulk_edit_url='dcim:frontport_bulk_edit' bulk_delete_url='dcim:frontport_bulk_delete' %}
|
||||
</div>
|
||||
<div class="col-md-3 noprint">
|
||||
{% include 'inc/search_panel.html' %}
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
17
netbox/templates/dcim/interface_list.html
Normal file
17
netbox/templates/dcim/interface_list.html
Normal file
@ -0,0 +1,17 @@
|
||||
{% extends '_base.html' %}
|
||||
{% load buttons %}
|
||||
|
||||
{% block content %}
|
||||
<div class="pull-right noprint">
|
||||
{% export_button content_type %}
|
||||
</div>
|
||||
<h1>{% block title %}Interfaces{% endblock %}</h1>
|
||||
<div class="row">
|
||||
<div class="col-md-9">
|
||||
{% include 'utilities/obj_table.html' with bulk_edit_url='dcim:interface_bulk_edit' bulk_delete_url='dcim:interface_bulk_delete' %}
|
||||
</div>
|
||||
<div class="col-md-3 noprint">
|
||||
{% include 'inc/search_panel.html' %}
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
17
netbox/templates/dcim/poweroutlet_list.html
Normal file
17
netbox/templates/dcim/poweroutlet_list.html
Normal file
@ -0,0 +1,17 @@
|
||||
{% extends '_base.html' %}
|
||||
{% load buttons %}
|
||||
|
||||
{% block content %}
|
||||
<div class="pull-right noprint">
|
||||
{% export_button content_type %}
|
||||
</div>
|
||||
<h1>{% block title %}Power Outlets{% endblock %}</h1>
|
||||
<div class="row">
|
||||
<div class="col-md-9">
|
||||
{% include 'utilities/obj_table.html' with bulk_edit_url='dcim:poweroutlet_bulk_edit' bulk_delete_url='dcim:poweroutlet_bulk_delete' %}
|
||||
</div>
|
||||
<div class="col-md-3 noprint">
|
||||
{% include 'inc/search_panel.html' %}
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
17
netbox/templates/dcim/powerport_list.html
Normal file
17
netbox/templates/dcim/powerport_list.html
Normal file
@ -0,0 +1,17 @@
|
||||
{% extends '_base.html' %}
|
||||
{% load buttons %}
|
||||
|
||||
{% block content %}
|
||||
<div class="pull-right noprint">
|
||||
{% export_button content_type %}
|
||||
</div>
|
||||
<h1>{% block title %}Power Ports{% endblock %}</h1>
|
||||
<div class="row">
|
||||
<div class="col-md-9">
|
||||
{% include 'utilities/obj_table.html' with bulk_edit_url='dcim:powerport_bulk_edit' bulk_delete_url='dcim:powerport_bulk_delete' %}
|
||||
</div>
|
||||
<div class="col-md-3 noprint">
|
||||
{% include 'inc/search_panel.html' %}
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
17
netbox/templates/dcim/rearport_list.html
Normal file
17
netbox/templates/dcim/rearport_list.html
Normal file
@ -0,0 +1,17 @@
|
||||
{% extends '_base.html' %}
|
||||
{% load buttons %}
|
||||
|
||||
{% block content %}
|
||||
<div class="pull-right noprint">
|
||||
{% export_button content_type %}
|
||||
</div>
|
||||
<h1>{% block title %}Rear Ports{% endblock %}</h1>
|
||||
<div class="row">
|
||||
<div class="col-md-9">
|
||||
{% include 'utilities/obj_table.html' with bulk_edit_url='dcim:rearport_bulk_edit' bulk_delete_url='dcim:rearport_bulk_delete' %}
|
||||
</div>
|
||||
<div class="col-md-3 noprint">
|
||||
{% include 'inc/search_panel.html' %}
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
@ -239,7 +239,7 @@
|
||||
<a href="{% url 'dcim:poweroutlet_import' %}" class="btn btn-xs btn-info" title="Import"><i class="fa fa-download"></i></a>
|
||||
</div>
|
||||
{% endif %}
|
||||
<a href="{% url 'dcim:poweroutlet_list' %}">Power Outlet</a>
|
||||
<a href="{% url 'dcim:poweroutlet_list' %}">Power Outlets</a>
|
||||
</li>
|
||||
<li{% if not perms.dcim.view_devicebay %} class="disabled"{% endif %}>
|
||||
{% if perms.dcim.add_devicebay %}
|
||||
|
Loading…
Reference in New Issue
Block a user