diff --git a/docs/release-notes/version-2.7.md b/docs/release-notes/version-2.7.md
index 1415be129..0aa62555b 100644
--- a/docs/release-notes/version-2.7.md
+++ b/docs/release-notes/version-2.7.md
@@ -6,6 +6,7 @@
* [#4100](https://github.com/netbox-community/netbox/issues/4100) - Add device filter to component list views
* [#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
+* [#4129](https://github.com/netbox-community/netbox/issues/4129) - Add buttons to delete individual device type components
## Bug Fixes
@@ -16,6 +17,8 @@
* [#4099](https://github.com/netbox-community/netbox/issues/4099) - Linkify interfaces on global interfaces list
* [#4108](https://github.com/netbox-community/netbox/issues/4108) - Avoid extraneous database queries when rendering search forms
+---
+
# v2.7.4 (2020-02-04)
## Enhancements
diff --git a/netbox/dcim/tables.py b/netbox/dcim/tables.py
index 473d465bd..1f67b93f1 100644
--- a/netbox/dcim/tables.py
+++ b/netbox/dcim/tables.py
@@ -200,6 +200,11 @@ def get_component_template_actions(model_name):
{{% endif %}}
+ {{% if perms.dcim.delete_{model_name} %}}
+
+
+
+ {{% endif %}}
""".format(model_name=model_name).strip()
diff --git a/netbox/dcim/tests/test_views.py b/netbox/dcim/tests/test_views.py
index f8282833c..75e3f9871 100644
--- a/netbox/dcim/tests/test_views.py
+++ b/netbox/dcim/tests/test_views.py
@@ -535,7 +535,6 @@ class ConsolePortTemplateTestCase(StandardTestCases.Views):
test_get_object = None
test_list_objects = None
test_create_object = None
- test_delete_object = None
test_import_objects = None
def test_bulk_create_objects(self):
@@ -580,7 +579,6 @@ class ConsoleServerPortTemplateTestCase(StandardTestCases.Views):
test_get_object = None
test_list_objects = None
test_create_object = None
- test_delete_object = None
test_import_objects = None
def test_bulk_create_objects(self):
@@ -625,7 +623,6 @@ class PowerPortTemplateTestCase(StandardTestCases.Views):
test_get_object = None
test_list_objects = None
test_create_object = None
- test_delete_object = None
test_import_objects = None
def test_bulk_create_objects(self):
@@ -676,7 +673,6 @@ class PowerOutletTemplateTestCase(StandardTestCases.Views):
test_get_object = None
test_list_objects = None
test_create_object = None
- test_delete_object = None
test_import_objects = None
def test_bulk_create_objects(self):
@@ -727,7 +723,6 @@ class InterfaceTemplateTestCase(StandardTestCases.Views):
test_get_object = None
test_list_objects = None
test_create_object = None
- test_delete_object = None
test_import_objects = None
def test_bulk_create_objects(self):
@@ -775,7 +770,6 @@ class FrontPortTemplateTestCase(StandardTestCases.Views):
test_get_object = None
test_list_objects = None
test_create_object = None
- test_delete_object = None
test_import_objects = None
def test_bulk_create_objects(self):
@@ -831,7 +825,6 @@ class RearPortTemplateTestCase(StandardTestCases.Views):
test_get_object = None
test_list_objects = None
test_create_object = None
- test_delete_object = None
test_import_objects = None
def test_bulk_create_objects(self):
@@ -878,7 +871,6 @@ class DeviceBayTemplateTestCase(StandardTestCases.Views):
test_get_object = None
test_list_objects = None
test_create_object = None
- test_delete_object = None
test_import_objects = None
test_bulk_edit_objects = None
diff --git a/netbox/dcim/urls.py b/netbox/dcim/urls.py
index 07d86cc36..165ca9e02 100644
--- a/netbox/dcim/urls.py
+++ b/netbox/dcim/urls.py
@@ -95,48 +95,56 @@ urlpatterns = [
path('console-port-templates/edit/', views.ConsolePortTemplateBulkEditView.as_view(), name='consoleporttemplate_bulk_edit'),
path('console-port-templates/delete/', views.ConsolePortTemplateBulkDeleteView.as_view(), name='consoleporttemplate_bulk_delete'),
path('console-port-templates//edit/', views.ConsolePortTemplateEditView.as_view(), name='consoleporttemplate_edit'),
+ path('console-port-templates//delete/', views.ConsolePortTemplateDeleteView.as_view(), name='consoleporttemplate_delete'),
# Console server port templates
path('console-server-port-templates/add/', views.ConsoleServerPortTemplateCreateView.as_view(), name='consoleserverporttemplate_add'),
path('console-server-port-templates/edit/', views.ConsoleServerPortTemplateBulkEditView.as_view(), name='consoleserverporttemplate_bulk_edit'),
path('console-server-port-templates/delete/', views.ConsoleServerPortTemplateBulkDeleteView.as_view(), name='consoleserverporttemplate_bulk_delete'),
path('console-server-port-templates//edit/', views.ConsoleServerPortTemplateEditView.as_view(), name='consoleserverporttemplate_edit'),
+ path('console-server-port-templates//delete/', views.ConsoleServerPortTemplateDeleteView.as_view(), name='consoleserverporttemplate_delete'),
# Power port templates
path('power-port-templates/add/', views.PowerPortTemplateCreateView.as_view(), name='powerporttemplate_add'),
path('power-port-templates/edit/', views.PowerPortTemplateBulkEditView.as_view(), name='powerporttemplate_bulk_edit'),
path('power-port-templates/delete/', views.PowerPortTemplateBulkDeleteView.as_view(), name='powerporttemplate_bulk_delete'),
path('power-port-templates//edit/', views.PowerPortTemplateEditView.as_view(), name='powerporttemplate_edit'),
+ path('power-port-templates//delete/', views.PowerPortTemplateDeleteView.as_view(), name='powerporttemplate_delete'),
# Power outlet templates
path('power-outlet-templates/add/', views.PowerOutletTemplateCreateView.as_view(), name='poweroutlettemplate_add'),
path('power-outlet-templates/edit/', views.PowerOutletTemplateBulkEditView.as_view(), name='poweroutlettemplate_bulk_edit'),
path('power-outlet-templates/delete/', views.PowerOutletTemplateBulkDeleteView.as_view(), name='poweroutlettemplate_bulk_delete'),
path('power-outlet-templates//edit/', views.PowerOutletTemplateEditView.as_view(), name='poweroutlettemplate_edit'),
+ path('power-outlet-templates//delete/', views.PowerOutletTemplateDeleteView.as_view(), name='poweroutlettemplate_delete'),
# Interface templates
path('interface-templates/add/', views.InterfaceTemplateCreateView.as_view(), name='interfacetemplate_add'),
path('interface-templates/edit/', views.InterfaceTemplateBulkEditView.as_view(), name='interfacetemplate_bulk_edit'),
path('interface-templates/delete/', views.InterfaceTemplateBulkDeleteView.as_view(), name='interfacetemplate_bulk_delete'),
path('interface-templates//edit/', views.InterfaceTemplateEditView.as_view(), name='interfacetemplate_edit'),
+ path('interface-templates//delete/', views.InterfaceTemplateDeleteView.as_view(), name='interfacetemplate_delete'),
# Front port templates
path('front-port-templates/add/', views.FrontPortTemplateCreateView.as_view(), name='frontporttemplate_add'),
path('front-port-templates/edit/', views.FrontPortTemplateBulkEditView.as_view(), name='frontporttemplate_bulk_edit'),
path('front-port-templates/delete/', views.FrontPortTemplateBulkDeleteView.as_view(), name='frontporttemplate_bulk_delete'),
path('front-port-templates//edit/', views.FrontPortTemplateEditView.as_view(), name='frontporttemplate_edit'),
+ path('front-port-templates//delete/', views.FrontPortTemplateDeleteView.as_view(), name='frontporttemplate_delete'),
# Rear port templates
path('rear-port-templates/add/', views.RearPortTemplateCreateView.as_view(), name='rearporttemplate_add'),
path('rear-port-templates/edit/', views.RearPortTemplateBulkEditView.as_view(), name='rearporttemplate_bulk_edit'),
path('rear-port-templates/delete/', views.RearPortTemplateBulkDeleteView.as_view(), name='rearporttemplate_bulk_delete'),
path('rear-port-templates//edit/', views.RearPortTemplateEditView.as_view(), name='rearporttemplate_edit'),
+ path('rear-port-templates//delete/', views.RearPortTemplateDeleteView.as_view(), name='rearporttemplate_delete'),
# Device bay templates
path('device-bay-templates/add/', views.DeviceBayTemplateCreateView.as_view(), name='devicebaytemplate_add'),
# path('device-bay-templates/edit/', views.DeviceBayTemplateBulkEditView.as_view(), name='devicebaytemplate_bulk_edit'),
path('device-bay-templates/delete/', views.DeviceBayTemplateBulkDeleteView.as_view(), name='devicebaytemplate_bulk_delete'),
path('device-bay-templates//edit/', views.DeviceBayTemplateEditView.as_view(), name='devicebaytemplate_edit'),
+ path('device-bay-templates//delete/', views.DeviceBayTemplateDeleteView.as_view(), name='devicebaytemplate_delete'),
# Device roles
path('device-roles/', views.DeviceRoleListView.as_view(), name='devicerole_list'),
diff --git a/netbox/dcim/views.py b/netbox/dcim/views.py
index 824961b3e..ae59890a3 100644
--- a/netbox/dcim/views.py
+++ b/netbox/dcim/views.py
@@ -700,7 +700,7 @@ class DeviceTypeBulkDeleteView(PermissionRequiredMixin, BulkDeleteView):
#
-# Device type components
+# Console port templates
#
class ConsolePortTemplateCreateView(PermissionRequiredMixin, ComponentCreateView):
@@ -717,6 +717,11 @@ class ConsolePortTemplateEditView(PermissionRequiredMixin, ObjectEditView):
model_form = forms.ConsolePortTemplateForm
+class ConsolePortTemplateDeleteView(PermissionRequiredMixin, ObjectDeleteView):
+ permission_required = 'dcim.delete_consoleporttemplate'
+ model = ConsolePortTemplate
+
+
class ConsolePortTemplateBulkEditView(PermissionRequiredMixin, BulkEditView):
permission_required = 'dcim.change_consoleporttemplate'
queryset = ConsolePortTemplate.objects.all()
@@ -730,6 +735,10 @@ class ConsolePortTemplateBulkDeleteView(PermissionRequiredMixin, BulkDeleteView)
table = tables.ConsolePortTemplateTable
+#
+# Console server port templates
+#
+
class ConsoleServerPortTemplateCreateView(PermissionRequiredMixin, ComponentCreateView):
permission_required = 'dcim.add_consoleserverporttemplate'
model = ConsoleServerPortTemplate
@@ -744,6 +753,11 @@ class ConsoleServerPortTemplateEditView(PermissionRequiredMixin, ObjectEditView)
model_form = forms.ConsoleServerPortTemplateForm
+class ConsoleServerPortTemplateDeleteView(PermissionRequiredMixin, ObjectDeleteView):
+ permission_required = 'dcim.delete_consoleserverporttemplate'
+ model = ConsoleServerPortTemplate
+
+
class ConsoleServerPortTemplateBulkEditView(PermissionRequiredMixin, BulkEditView):
permission_required = 'dcim.change_consoleserverporttemplate'
queryset = ConsoleServerPortTemplate.objects.all()
@@ -757,6 +771,10 @@ class ConsoleServerPortTemplateBulkDeleteView(PermissionRequiredMixin, BulkDelet
table = tables.ConsoleServerPortTemplateTable
+#
+# Power port templates
+#
+
class PowerPortTemplateCreateView(PermissionRequiredMixin, ComponentCreateView):
permission_required = 'dcim.add_powerporttemplate'
model = PowerPortTemplate
@@ -771,6 +789,11 @@ class PowerPortTemplateEditView(PermissionRequiredMixin, ObjectEditView):
model_form = forms.PowerPortTemplateForm
+class PowerPortTemplateDeleteView(PermissionRequiredMixin, ObjectDeleteView):
+ permission_required = 'dcim.delete_powerporttemplate'
+ model = PowerPortTemplate
+
+
class PowerPortTemplateBulkEditView(PermissionRequiredMixin, BulkEditView):
permission_required = 'dcim.change_powerporttemplate'
queryset = PowerPortTemplate.objects.all()
@@ -784,6 +807,10 @@ class PowerPortTemplateBulkDeleteView(PermissionRequiredMixin, BulkDeleteView):
table = tables.PowerPortTemplateTable
+#
+# Power outlet templates
+#
+
class PowerOutletTemplateCreateView(PermissionRequiredMixin, ComponentCreateView):
permission_required = 'dcim.add_poweroutlettemplate'
model = PowerOutletTemplate
@@ -798,6 +825,11 @@ class PowerOutletTemplateEditView(PermissionRequiredMixin, ObjectEditView):
model_form = forms.PowerOutletTemplateForm
+class PowerOutletTemplateDeleteView(PermissionRequiredMixin, ObjectDeleteView):
+ permission_required = 'dcim.delete_poweroutlettemplate'
+ model = PowerOutletTemplate
+
+
class PowerOutletTemplateBulkEditView(PermissionRequiredMixin, BulkEditView):
permission_required = 'dcim.change_poweroutlettemplate'
queryset = PowerOutletTemplate.objects.all()
@@ -811,6 +843,10 @@ class PowerOutletTemplateBulkDeleteView(PermissionRequiredMixin, BulkDeleteView)
table = tables.PowerOutletTemplateTable
+#
+# Interface templates
+#
+
class InterfaceTemplateCreateView(PermissionRequiredMixin, ComponentCreateView):
permission_required = 'dcim.add_interfacetemplate'
model = InterfaceTemplate
@@ -825,6 +861,11 @@ class InterfaceTemplateEditView(PermissionRequiredMixin, ObjectEditView):
model_form = forms.InterfaceTemplateForm
+class InterfaceTemplateDeleteView(PermissionRequiredMixin, ObjectDeleteView):
+ permission_required = 'dcim.delete_interfacetemplate'
+ model = InterfaceTemplate
+
+
class InterfaceTemplateBulkEditView(PermissionRequiredMixin, BulkEditView):
permission_required = 'dcim.change_interfacetemplate'
queryset = InterfaceTemplate.objects.all()
@@ -838,6 +879,10 @@ class InterfaceTemplateBulkDeleteView(PermissionRequiredMixin, BulkDeleteView):
table = tables.InterfaceTemplateTable
+#
+# Front port templates
+#
+
class FrontPortTemplateCreateView(PermissionRequiredMixin, ComponentCreateView):
permission_required = 'dcim.add_frontporttemplate'
model = FrontPortTemplate
@@ -852,6 +897,11 @@ class FrontPortTemplateEditView(PermissionRequiredMixin, ObjectEditView):
model_form = forms.FrontPortTemplateForm
+class FrontPortTemplateDeleteView(PermissionRequiredMixin, ObjectDeleteView):
+ permission_required = 'dcim.delete_frontporttemplate'
+ model = FrontPortTemplate
+
+
class FrontPortTemplateBulkEditView(PermissionRequiredMixin, BulkEditView):
permission_required = 'dcim.change_frontporttemplate'
queryset = FrontPortTemplate.objects.all()
@@ -865,6 +915,10 @@ class FrontPortTemplateBulkDeleteView(PermissionRequiredMixin, BulkDeleteView):
table = tables.FrontPortTemplateTable
+#
+# Rear port templates
+#
+
class RearPortTemplateCreateView(PermissionRequiredMixin, ComponentCreateView):
permission_required = 'dcim.add_rearporttemplate'
model = RearPortTemplate
@@ -879,6 +933,11 @@ class RearPortTemplateEditView(PermissionRequiredMixin, ObjectEditView):
model_form = forms.RearPortTemplateForm
+class RearPortTemplateDeleteView(PermissionRequiredMixin, ObjectDeleteView):
+ permission_required = 'dcim.delete_rearporttemplate'
+ model = RearPortTemplate
+
+
class RearPortTemplateBulkEditView(PermissionRequiredMixin, BulkEditView):
permission_required = 'dcim.change_rearporttemplate'
queryset = RearPortTemplate.objects.all()
@@ -892,6 +951,10 @@ class RearPortTemplateBulkDeleteView(PermissionRequiredMixin, BulkDeleteView):
table = tables.RearPortTemplateTable
+#
+# Device bay templates
+#
+
class DeviceBayTemplateCreateView(PermissionRequiredMixin, ComponentCreateView):
permission_required = 'dcim.add_devicebaytemplate'
model = DeviceBayTemplate
@@ -906,6 +969,11 @@ class DeviceBayTemplateEditView(PermissionRequiredMixin, ObjectEditView):
model_form = forms.DeviceBayTemplateForm
+class DeviceBayTemplateDeleteView(PermissionRequiredMixin, ObjectDeleteView):
+ permission_required = 'dcim.delete_devicebaytemplate'
+ model = DeviceBayTemplate
+
+
# class DeviceBayTemplateBulkEditView(PermissionRequiredMixin, BulkEditView):
# permission_required = 'dcim.change_devicebaytemplate'
# queryset = DeviceBayTemplate.objects.all()