Просмотр исходного кода

Cleaned up BulkEditView and BulkDeleteView

Jeremy Stretch 7 лет назад
Родитель
Сommit
beac676a6e

+ 2 - 5
netbox/circuits/views.py

@@ -76,7 +76,7 @@ class ProviderBulkImportView(PermissionRequiredMixin, BulkImportView):
 
 class ProviderBulkEditView(PermissionRequiredMixin, BulkEditView):
     permission_required = 'circuits.change_provider'
-    cls = Provider
+    queryset = Provider.objects.all()
     filter = filters.ProviderFilter
     table = tables.ProviderTable
     form = forms.ProviderBulkEditForm
@@ -85,7 +85,7 @@ class ProviderBulkEditView(PermissionRequiredMixin, BulkEditView):
 
 class ProviderBulkDeleteView(PermissionRequiredMixin, BulkDeleteView):
     permission_required = 'circuits.delete_provider'
-    cls = Provider
+    queryset = Provider.objects.all()
     filter = filters.ProviderFilter
     table = tables.ProviderTable
     default_return_url = 'circuits:provider_list'
@@ -121,7 +121,6 @@ class CircuitTypeBulkImportView(PermissionRequiredMixin, BulkImportView):
 
 class CircuitTypeBulkDeleteView(PermissionRequiredMixin, BulkDeleteView):
     permission_required = 'circuits.delete_circuittype'
-    cls = CircuitType
     queryset = CircuitType.objects.annotate(circuit_count=Count('circuits'))
     table = tables.CircuitTypeTable
     default_return_url = 'circuits:circuittype_list'
@@ -193,7 +192,6 @@ class CircuitBulkImportView(PermissionRequiredMixin, BulkImportView):
 
 class CircuitBulkEditView(PermissionRequiredMixin, BulkEditView):
     permission_required = 'circuits.change_circuit'
-    cls = Circuit
     queryset = Circuit.objects.select_related('provider', 'type', 'tenant').prefetch_related('terminations__site')
     filter = filters.CircuitFilter
     table = tables.CircuitTable
@@ -203,7 +201,6 @@ class CircuitBulkEditView(PermissionRequiredMixin, BulkEditView):
 
 class CircuitBulkDeleteView(PermissionRequiredMixin, BulkDeleteView):
     permission_required = 'circuits.delete_circuit'
-    cls = Circuit
     queryset = Circuit.objects.select_related('provider', 'type', 'tenant').prefetch_related('terminations__site')
     filter = filters.CircuitFilter
     table = tables.CircuitTable

+ 30 - 48
netbox/dcim/views.py

@@ -151,7 +151,6 @@ class RegionBulkImportView(PermissionRequiredMixin, BulkImportView):
 
 class RegionBulkDeleteView(PermissionRequiredMixin, BulkDeleteView):
     permission_required = 'dcim.delete_region'
-    cls = Region
     queryset = Region.objects.annotate(site_count=Count('sites'))
     filter = filters.RegionFilter
     table = tables.RegionTable
@@ -223,7 +222,6 @@ class SiteBulkImportView(PermissionRequiredMixin, BulkImportView):
 
 class SiteBulkEditView(PermissionRequiredMixin, BulkEditView):
     permission_required = 'dcim.change_site'
-    cls = Site
     queryset = Site.objects.select_related('region', 'tenant')
     filter = filters.SiteFilter
     table = tables.SiteTable
@@ -263,7 +261,6 @@ class RackGroupBulkImportView(PermissionRequiredMixin, BulkImportView):
 
 class RackGroupBulkDeleteView(PermissionRequiredMixin, BulkDeleteView):
     permission_required = 'dcim.delete_rackgroup'
-    cls = RackGroup
     queryset = RackGroup.objects.select_related('site').annotate(rack_count=Count('racks'))
     filter = filters.RackGroupFilter
     table = tables.RackGroupTable
@@ -300,7 +297,6 @@ class RackRoleBulkImportView(PermissionRequiredMixin, BulkImportView):
 
 class RackRoleBulkDeleteView(PermissionRequiredMixin, BulkDeleteView):
     permission_required = 'dcim.delete_rackrole'
-    cls = RackRole
     queryset = RackRole.objects.annotate(rack_count=Count('racks'))
     table = tables.RackRoleTable
     default_return_url = 'dcim:rackrole_list'
@@ -415,7 +411,6 @@ class RackBulkImportView(PermissionRequiredMixin, BulkImportView):
 
 class RackBulkEditView(PermissionRequiredMixin, BulkEditView):
     permission_required = 'dcim.change_rack'
-    cls = Rack
     queryset = Rack.objects.select_related('site', 'group', 'tenant', 'role')
     filter = filters.RackFilter
     table = tables.RackTable
@@ -425,7 +420,6 @@ class RackBulkEditView(PermissionRequiredMixin, BulkEditView):
 
 class RackBulkDeleteView(PermissionRequiredMixin, BulkDeleteView):
     permission_required = 'dcim.delete_rack'
-    cls = Rack
     queryset = Rack.objects.select_related('site', 'group', 'tenant', 'role')
     filter = filters.RackFilter
     table = tables.RackTable
@@ -473,7 +467,6 @@ class RackReservationDeleteView(PermissionRequiredMixin, ObjectDeleteView):
 
 class RackReservationBulkEditView(PermissionRequiredMixin, BulkEditView):
     permission_required = 'dcim.change_rackreservation'
-    cls = RackReservation
     queryset = RackReservation.objects.select_related('rack', 'user')
     filter = filters.RackReservationFilter
     table = tables.RackReservationTable
@@ -483,7 +476,7 @@ class RackReservationBulkEditView(PermissionRequiredMixin, BulkEditView):
 
 class RackReservationBulkDeleteView(PermissionRequiredMixin, BulkDeleteView):
     permission_required = 'dcim.delete_rackreservation'
-    cls = RackReservation
+    queryset = RackReservation.objects.select_related('rack', 'user')
     filter = filters.RackReservationFilter
     table = tables.RackReservationTable
     default_return_url = 'dcim:rackreservation_list'
@@ -522,7 +515,6 @@ class ManufacturerBulkImportView(PermissionRequiredMixin, BulkImportView):
 
 class ManufacturerBulkDeleteView(PermissionRequiredMixin, BulkDeleteView):
     permission_required = 'dcim.delete_manufacturer'
-    cls = Manufacturer
     queryset = Manufacturer.objects.annotate(devicetype_count=Count('device_types'))
     table = tables.ManufacturerTable
     default_return_url = 'dcim:manufacturer_list'
@@ -619,7 +611,6 @@ class DeviceTypeBulkImportView(PermissionRequiredMixin, BulkImportView):
 
 class DeviceTypeBulkEditView(PermissionRequiredMixin, BulkEditView):
     permission_required = 'dcim.change_devicetype'
-    cls = DeviceType
     queryset = DeviceType.objects.select_related('manufacturer').annotate(instance_count=Count('instances'))
     filter = filters.DeviceTypeFilter
     table = tables.DeviceTypeTable
@@ -629,7 +620,6 @@ class DeviceTypeBulkEditView(PermissionRequiredMixin, BulkEditView):
 
 class DeviceTypeBulkDeleteView(PermissionRequiredMixin, BulkDeleteView):
     permission_required = 'dcim.delete_devicetype'
-    cls = DeviceType
     queryset = DeviceType.objects.select_related('manufacturer').annotate(instance_count=Count('instances'))
     filter = filters.DeviceTypeFilter
     table = tables.DeviceTypeTable
@@ -652,10 +642,8 @@ class ConsolePortTemplateCreateView(PermissionRequiredMixin, ComponentCreateView
 
 class ConsolePortTemplateBulkDeleteView(PermissionRequiredMixin, BulkDeleteView):
     permission_required = 'dcim.delete_consoleporttemplate'
+    queryset = ConsolePortTemplate.objects.all()
     parent_model = DeviceType
-    parent_field = 'device_type'
-    cls = ConsolePortTemplate
-    parent_cls = DeviceType
     table = tables.ConsolePortTemplateTable
 
 
@@ -671,8 +659,8 @@ class ConsoleServerPortTemplateCreateView(PermissionRequiredMixin, ComponentCrea
 
 class ConsoleServerPortTemplateBulkDeleteView(PermissionRequiredMixin, BulkDeleteView):
     permission_required = 'dcim.delete_consoleserverporttemplate'
-    cls = ConsoleServerPortTemplate
-    parent_cls = DeviceType
+    queryset = ConsoleServerPortTemplate.objects.all()
+    parent_model = DeviceType
     table = tables.ConsoleServerPortTemplateTable
 
 
@@ -688,8 +676,8 @@ class PowerPortTemplateCreateView(PermissionRequiredMixin, ComponentCreateView):
 
 class PowerPortTemplateBulkDeleteView(PermissionRequiredMixin, BulkDeleteView):
     permission_required = 'dcim.delete_powerporttemplate'
-    cls = PowerPortTemplate
-    parent_cls = DeviceType
+    queryset = PowerPortTemplate.objects.all()
+    parent_model = DeviceType
     table = tables.PowerPortTemplateTable
 
 
@@ -705,8 +693,8 @@ class PowerOutletTemplateCreateView(PermissionRequiredMixin, ComponentCreateView
 
 class PowerOutletTemplateBulkDeleteView(PermissionRequiredMixin, BulkDeleteView):
     permission_required = 'dcim.delete_poweroutlettemplate'
-    cls = PowerOutletTemplate
-    parent_cls = DeviceType
+    queryset = PowerOutletTemplate.objects.all()
+    parent_model = DeviceType
     table = tables.PowerOutletTemplateTable
 
 
@@ -722,16 +710,16 @@ class InterfaceTemplateCreateView(PermissionRequiredMixin, ComponentCreateView):
 
 class InterfaceTemplateBulkEditView(PermissionRequiredMixin, BulkEditView):
     permission_required = 'dcim.change_interfacetemplate'
-    cls = InterfaceTemplate
-    parent_cls = DeviceType
+    queryset = InterfaceTemplate.objects.all()
+    parent_model = DeviceType
     table = tables.InterfaceTemplateTable
     form = forms.InterfaceTemplateBulkEditForm
 
 
 class InterfaceTemplateBulkDeleteView(PermissionRequiredMixin, BulkDeleteView):
     permission_required = 'dcim.delete_interfacetemplate'
-    cls = InterfaceTemplate
-    parent_cls = DeviceType
+    queryset = InterfaceTemplate.objects.all()
+    parent_model = DeviceType
     table = tables.InterfaceTemplateTable
 
 
@@ -747,8 +735,8 @@ class DeviceBayTemplateCreateView(PermissionRequiredMixin, ComponentCreateView):
 
 class DeviceBayTemplateBulkDeleteView(PermissionRequiredMixin, BulkDeleteView):
     permission_required = 'dcim.delete_devicebaytemplate'
-    cls = DeviceBayTemplate
-    parent_cls = DeviceType
+    queryset = DeviceBayTemplate.objects.all()
+    parent_model = DeviceType
     table = tables.DeviceBayTemplateTable
 
 
@@ -782,8 +770,7 @@ class DeviceRoleBulkImportView(PermissionRequiredMixin, BulkImportView):
 
 class DeviceRoleBulkDeleteView(PermissionRequiredMixin, BulkDeleteView):
     permission_required = 'dcim.delete_devicerole'
-    cls = DeviceRole
-    queryset = DeviceRole.objects.annotate(device_count=Count('devices'))
+    queryset = DeviceRole.objects.all()
     table = tables.DeviceRoleTable
     default_return_url = 'dcim:devicerole_list'
 
@@ -818,8 +805,7 @@ class PlatformBulkImportView(PermissionRequiredMixin, BulkImportView):
 
 class PlatformBulkDeleteView(PermissionRequiredMixin, BulkDeleteView):
     permission_required = 'dcim.delete_platform'
-    cls = Platform
-    queryset = Platform.objects.annotate(device_count=Count('devices'))
+    queryset = Platform.objects.all()
     table = tables.PlatformTable
     default_return_url = 'dcim:platform_list'
 
@@ -1032,7 +1018,6 @@ class ChildDeviceBulkImportView(PermissionRequiredMixin, BulkImportView):
 
 class DeviceBulkEditView(PermissionRequiredMixin, BulkEditView):
     permission_required = 'dcim.change_device'
-    cls = Device
     queryset = Device.objects.select_related('tenant', 'site', 'rack', 'device_role', 'device_type__manufacturer')
     filter = filters.DeviceFilter
     table = tables.DeviceTable
@@ -1042,7 +1027,6 @@ class DeviceBulkEditView(PermissionRequiredMixin, BulkEditView):
 
 class DeviceBulkDeleteView(PermissionRequiredMixin, BulkDeleteView):
     permission_required = 'dcim.delete_device'
-    cls = Device
     queryset = Device.objects.select_related('tenant', 'site', 'rack', 'device_role', 'device_type__manufacturer')
     filter = filters.DeviceFilter
     table = tables.DeviceTable
@@ -1172,8 +1156,8 @@ class ConsolePortDeleteView(PermissionRequiredMixin, ObjectDeleteView):
 
 class ConsolePortBulkDeleteView(PermissionRequiredMixin, BulkDeleteView):
     permission_required = 'dcim.delete_consoleport'
-    cls = ConsolePort
-    parent_cls = Device
+    queryset = ConsolePort.objects.all()
+    parent_model = Device
     table = tables.ConsolePortTable
 
 
@@ -1326,8 +1310,8 @@ class ConsoleServerPortBulkDisconnectView(PermissionRequiredMixin, BulkDisconnec
 
 class ConsoleServerPortBulkDeleteView(PermissionRequiredMixin, BulkDeleteView):
     permission_required = 'dcim.delete_consoleserverport'
-    cls = ConsoleServerPort
-    parent_cls = Device
+    queryset = ConsoleServerPort.objects.all()
+    parent_model = Device
     table = tables.ConsoleServerPortTable
 
 
@@ -1454,8 +1438,8 @@ class PowerPortDeleteView(PermissionRequiredMixin, ObjectDeleteView):
 
 class PowerPortBulkDeleteView(PermissionRequiredMixin, BulkDeleteView):
     permission_required = 'dcim.delete_powerport'
-    cls = PowerPort
-    parent_cls = Device
+    queryset = PowerPort.objects.all()
+    parent_model = Device
     table = tables.PowerPortTable
 
 
@@ -1608,8 +1592,8 @@ class PowerOutletBulkDisconnectView(PermissionRequiredMixin, BulkDisconnectView)
 
 class PowerOutletBulkDeleteView(PermissionRequiredMixin, BulkDeleteView):
     permission_required = 'dcim.delete_poweroutlet'
-    cls = PowerOutlet
-    parent_cls = Device
+    queryset = PowerOutlet.objects.all()
+    parent_model = Device
     table = tables.PowerOutletTable
 
 
@@ -1700,8 +1684,8 @@ class InterfaceBulkDisconnectView(PermissionRequiredMixin, BulkDisconnectView):
 
 class InterfaceBulkEditView(PermissionRequiredMixin, BulkEditView):
     permission_required = 'dcim.change_interface'
-    cls = Interface
-    parent_cls = Device
+    queryset = Interface.objects.all()
+    parent_model = Device
     table = tables.InterfaceTable
     form = forms.InterfaceBulkEditForm
 
@@ -1714,8 +1698,8 @@ class InterfaceBulkRenameView(PermissionRequiredMixin, BulkRenameView):
 
 class InterfaceBulkDeleteView(PermissionRequiredMixin, BulkDeleteView):
     permission_required = 'dcim.delete_interface'
-    cls = Interface
-    parent_cls = Device
+    queryset = Interface.objects.all()
+    parent_model = Device
     table = tables.InterfaceTable
 
 
@@ -1821,8 +1805,8 @@ class DeviceBayBulkRenameView(PermissionRequiredMixin, BulkRenameView):
 
 class DeviceBayBulkDeleteView(PermissionRequiredMixin, BulkDeleteView):
     permission_required = 'dcim.delete_devicebay'
-    cls = DeviceBay
-    parent_cls = Device
+    queryset = DeviceBay.objects.all()
+    parent_model = Device
     table = tables.DeviceBayTable
 
 
@@ -2079,7 +2063,6 @@ class InventoryItemBulkImportView(PermissionRequiredMixin, BulkImportView):
 
 class InventoryItemBulkEditView(PermissionRequiredMixin, BulkEditView):
     permission_required = 'dcim.change_inventoryitem'
-    cls = InventoryItem
     queryset = InventoryItem.objects.select_related('device', 'manufacturer')
     filter = filters.InventoryItemFilter
     table = tables.InventoryItemTable
@@ -2089,7 +2072,6 @@ class InventoryItemBulkEditView(PermissionRequiredMixin, BulkEditView):
 
 class InventoryItemBulkDeleteView(PermissionRequiredMixin, BulkDeleteView):
     permission_required = 'dcim.delete_inventoryitem'
-    cls = InventoryItem
     queryset = InventoryItem.objects.select_related('device', 'manufacturer')
     table = tables.InventoryItemTable
     template_name = 'dcim/inventoryitem_bulk_delete.html'

+ 0 - 2
netbox/extras/views.py

@@ -45,7 +45,6 @@ class TagDeleteView(PermissionRequiredMixin, ObjectDeleteView):
 
 class TagBulkDeleteView(PermissionRequiredMixin, BulkDeleteView):
     permission_required = 'circuits.delete_circuittype'
-    cls = Tag
     queryset = Tag.objects.annotate(items=Count('taggit_taggeditem_items')).order_by('name')
     table = TagTable
     default_return_url = 'extras:tag_list'
@@ -92,7 +91,6 @@ class ConfigContextDeleteView(PermissionRequiredMixin, ObjectDeleteView):
 
 class ConfigContextBulkDeleteView(PermissionRequiredMixin, BulkDeleteView):
     permission_required = 'extras.delete_cconfigcontext'
-    cls = ConfigContext
     queryset = ConfigContext.objects.all()
     table = ConfigContextTable
     default_return_url = 'extras:configcontext_list'

+ 3 - 17
netbox/ipam/views.py

@@ -138,7 +138,6 @@ class VRFBulkImportView(PermissionRequiredMixin, BulkImportView):
 
 class VRFBulkEditView(PermissionRequiredMixin, BulkEditView):
     permission_required = 'ipam.change_vrf'
-    cls = VRF
     queryset = VRF.objects.select_related('tenant')
     filter = filters.VRFFilter
     table = tables.VRFTable
@@ -148,7 +147,6 @@ class VRFBulkEditView(PermissionRequiredMixin, BulkEditView):
 
 class VRFBulkDeleteView(PermissionRequiredMixin, BulkDeleteView):
     permission_required = 'ipam.delete_vrf'
-    cls = VRF
     queryset = VRF.objects.select_related('tenant')
     filter = filters.VRFFilter
     table = tables.VRFTable
@@ -263,7 +261,6 @@ class RIRBulkImportView(PermissionRequiredMixin, BulkImportView):
 
 class RIRBulkDeleteView(PermissionRequiredMixin, BulkDeleteView):
     permission_required = 'ipam.delete_rir'
-    cls = RIR
     queryset = RIR.objects.annotate(aggregate_count=Count('aggregates'))
     filter = filters.RIRFilter
     table = tables.RIRTable
@@ -367,7 +364,6 @@ class AggregateBulkImportView(PermissionRequiredMixin, BulkImportView):
 
 class AggregateBulkEditView(PermissionRequiredMixin, BulkEditView):
     permission_required = 'ipam.change_aggregate'
-    cls = Aggregate
     queryset = Aggregate.objects.select_related('rir')
     filter = filters.AggregateFilter
     table = tables.AggregateTable
@@ -377,7 +373,6 @@ class AggregateBulkEditView(PermissionRequiredMixin, BulkEditView):
 
 class AggregateBulkDeleteView(PermissionRequiredMixin, BulkDeleteView):
     permission_required = 'ipam.delete_aggregate'
-    cls = Aggregate
     queryset = Aggregate.objects.select_related('rir')
     filter = filters.AggregateFilter
     table = tables.AggregateTable
@@ -414,7 +409,7 @@ class RoleBulkImportView(PermissionRequiredMixin, BulkImportView):
 
 class RoleBulkDeleteView(PermissionRequiredMixin, BulkDeleteView):
     permission_required = 'ipam.delete_role'
-    cls = Role
+    queryset = Role.objects.all()
     table = tables.RoleTable
     default_return_url = 'ipam:role_list'
 
@@ -588,7 +583,6 @@ class PrefixBulkImportView(PermissionRequiredMixin, BulkImportView):
 
 class PrefixBulkEditView(PermissionRequiredMixin, BulkEditView):
     permission_required = 'ipam.change_prefix'
-    cls = Prefix
     queryset = Prefix.objects.select_related('site', 'vrf__tenant', 'tenant', 'vlan', 'role')
     filter = filters.PrefixFilter
     table = tables.PrefixTable
@@ -598,7 +592,6 @@ class PrefixBulkEditView(PermissionRequiredMixin, BulkEditView):
 
 class PrefixBulkDeleteView(PermissionRequiredMixin, BulkDeleteView):
     permission_required = 'ipam.delete_prefix'
-    cls = Prefix
     queryset = Prefix.objects.select_related('site', 'vrf__tenant', 'tenant', 'vlan', 'role')
     filter = filters.PrefixFilter
     table = tables.PrefixTable
@@ -761,7 +754,6 @@ class IPAddressBulkImportView(PermissionRequiredMixin, BulkImportView):
 
 class IPAddressBulkEditView(PermissionRequiredMixin, BulkEditView):
     permission_required = 'ipam.change_ipaddress'
-    cls = IPAddress
     queryset = IPAddress.objects.select_related('vrf__tenant', 'tenant').prefetch_related('interface__device')
     filter = filters.IPAddressFilter
     table = tables.IPAddressTable
@@ -771,7 +763,6 @@ class IPAddressBulkEditView(PermissionRequiredMixin, BulkEditView):
 
 class IPAddressBulkDeleteView(PermissionRequiredMixin, BulkDeleteView):
     permission_required = 'ipam.delete_ipaddress'
-    cls = IPAddress
     queryset = IPAddress.objects.select_related('vrf__tenant', 'tenant').prefetch_related('interface__device')
     filter = filters.IPAddressFilter
     table = tables.IPAddressTable
@@ -810,7 +801,6 @@ class VLANGroupBulkImportView(PermissionRequiredMixin, BulkImportView):
 
 class VLANGroupBulkDeleteView(PermissionRequiredMixin, BulkDeleteView):
     permission_required = 'ipam.delete_vlangroup'
-    cls = VLANGroup
     queryset = VLANGroup.objects.select_related('site').annotate(vlan_count=Count('vlans'))
     filter = filters.VLANGroupFilter
     table = tables.VLANGroupTable
@@ -895,7 +885,6 @@ class VLANBulkImportView(PermissionRequiredMixin, BulkImportView):
 
 class VLANBulkEditView(PermissionRequiredMixin, BulkEditView):
     permission_required = 'ipam.change_vlan'
-    cls = VLAN
     queryset = VLAN.objects.select_related('site', 'group', 'tenant', 'role')
     filter = filters.VLANFilter
     table = tables.VLANTable
@@ -905,7 +894,6 @@ class VLANBulkEditView(PermissionRequiredMixin, BulkEditView):
 
 class VLANBulkDeleteView(PermissionRequiredMixin, BulkDeleteView):
     permission_required = 'ipam.delete_vlan'
-    cls = VLAN
     queryset = VLAN.objects.select_related('site', 'group', 'tenant', 'role')
     filter = filters.VLANFilter
     table = tables.VLANTable
@@ -960,8 +948,7 @@ class ServiceDeleteView(PermissionRequiredMixin, ObjectDeleteView):
 
 class ServiceBulkEditView(PermissionRequiredMixin, BulkEditView):
     permission_required = 'ipam.change_service'
-    cls = Service
-    queryset = Service.objects.all()
+    queryset = Service.objects.select_related('device', 'virtual_machine')
     filter = filters.ServiceFilter
     table = tables.ServiceTable
     form = forms.ServiceBulkEditForm
@@ -970,8 +957,7 @@ class ServiceBulkEditView(PermissionRequiredMixin, BulkEditView):
 
 class ServiceBulkDeleteView(PermissionRequiredMixin, BulkDeleteView):
     permission_required = 'ipam.delete_service'
-    cls = Service
-    queryset = Service.objects.all()
+    queryset = Service.objects.select_related('device', 'virtual_machine')
     filter = filters.ServiceFilter
     table = tables.ServiceTable
     default_return_url = 'ipam:service_list'

+ 0 - 3
netbox/secrets/views.py

@@ -60,7 +60,6 @@ class SecretRoleBulkImportView(PermissionRequiredMixin, BulkImportView):
 
 class SecretRoleBulkDeleteView(PermissionRequiredMixin, BulkDeleteView):
     permission_required = 'secrets.delete_secretrole'
-    cls = SecretRole
     queryset = SecretRole.objects.annotate(secret_count=Count('secrets'))
     table = tables.SecretRoleTable
     default_return_url = 'secrets:secretrole_list'
@@ -248,7 +247,6 @@ class SecretBulkImportView(BulkImportView):
 
 class SecretBulkEditView(PermissionRequiredMixin, BulkEditView):
     permission_required = 'secrets.change_secret'
-    cls = Secret
     queryset = Secret.objects.select_related('role', 'device')
     filter = filters.SecretFilter
     table = tables.SecretTable
@@ -258,7 +256,6 @@ class SecretBulkEditView(PermissionRequiredMixin, BulkEditView):
 
 class SecretBulkDeleteView(PermissionRequiredMixin, BulkDeleteView):
     permission_required = 'secrets.delete_secret'
-    cls = Secret
     queryset = Secret.objects.select_related('role', 'device')
     filter = filters.SecretFilter
     table = tables.SecretTable

+ 0 - 3
netbox/tenancy/views.py

@@ -46,7 +46,6 @@ class TenantGroupBulkImportView(PermissionRequiredMixin, BulkImportView):
 
 class TenantGroupBulkDeleteView(PermissionRequiredMixin, BulkDeleteView):
     permission_required = 'tenancy.delete_tenantgroup'
-    cls = TenantGroup
     queryset = TenantGroup.objects.annotate(tenant_count=Count('tenants'))
     table = tables.TenantGroupTable
     default_return_url = 'tenancy:tenantgroup_list'
@@ -115,7 +114,6 @@ class TenantBulkImportView(PermissionRequiredMixin, BulkImportView):
 
 class TenantBulkEditView(PermissionRequiredMixin, BulkEditView):
     permission_required = 'tenancy.change_tenant'
-    cls = Tenant
     queryset = Tenant.objects.select_related('group')
     filter = filters.TenantFilter
     table = tables.TenantTable
@@ -125,7 +123,6 @@ class TenantBulkEditView(PermissionRequiredMixin, BulkEditView):
 
 class TenantBulkDeleteView(PermissionRequiredMixin, BulkDeleteView):
     permission_required = 'tenancy.delete_tenant'
-    cls = Tenant
     queryset = Tenant.objects.select_related('group')
     filter = filters.TenantFilter
     table = tables.TenantTable

+ 31 - 32
netbox/utilities/views.py

@@ -301,6 +301,7 @@ class BulkCreateView(GetReturnURLMixin, View):
 
     form: Form class which provides the `pattern` field
     model_form: The ModelForm used to create individual objects
+    pattern_target: Name of the field to be evaluated as a pattern (if any)
     template_name: The name of the template
     """
     form = None
@@ -466,17 +467,15 @@ class BulkEditView(GetReturnURLMixin, View):
     """
     Edit objects in bulk.
 
-    cls: The model of the objects being edited
-    parent_cls: The model of the parent object (if any)
     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
     """
-    cls = None
-    parent_cls = None
     queryset = None
+    parent_model = None
     filter = None
     table = None
     form = None
@@ -487,20 +486,22 @@ class BulkEditView(GetReturnURLMixin, View):
 
     def post(self, request, **kwargs):
 
+        model = self.queryset.model
+
         # Attempt to derive parent object if a parent class has been given
-        if self.parent_cls:
-            parent_obj = get_object_or_404(self.parent_cls, **kwargs)
+        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.filter is not None:
-            pk_list = [obj.pk for obj in self.filter(request.GET, self.cls.objects.only('pk')).qs]
+            pk_list = [obj.pk for obj in self.filter(request.GET, model.objects.only('pk')).qs]
         else:
             pk_list = [int(pk) for pk in request.POST.getlist('pk')]
 
         if '_apply' in request.POST:
-            form = self.form(self.cls, parent_obj, request.POST)
+            form = self.form(model, parent_obj, request.POST)
             if form.is_valid():
 
                 custom_fields = form.custom_fields if hasattr(form, 'custom_fields') else []
@@ -512,7 +513,7 @@ class BulkEditView(GetReturnURLMixin, View):
                     with transaction.atomic():
 
                         updated_count = 0
-                        for obj in self.cls.objects.filter(pk__in=pk_list):
+                        for obj in model.objects.filter(pk__in=pk_list):
 
                             # Update standard fields. If a field is listed in _nullify, delete its value.
                             for name in standard_fields:
@@ -524,7 +525,7 @@ class BulkEditView(GetReturnURLMixin, View):
                             obj.save()
 
                             # Update custom fields
-                            obj_type = ContentType.objects.get_for_model(self.cls)
+                            obj_type = ContentType.objects.get_for_model(model)
                             for name in custom_fields:
                                 field = form.fields[name].model
                                 if name in form.nullable_fields and name in nullified_fields:
@@ -552,7 +553,7 @@ class BulkEditView(GetReturnURLMixin, View):
                             updated_count += 1
 
                     if updated_count:
-                        msg = 'Updated {} {}'.format(updated_count, self.cls._meta.verbose_name_plural)
+                        msg = 'Updated {} {}'.format(updated_count, model._meta.verbose_name_plural)
                         messages.success(self.request, msg)
 
                     return redirect(self.get_return_url(request))
@@ -563,19 +564,18 @@ class BulkEditView(GetReturnURLMixin, View):
         else:
             initial_data = request.POST.copy()
             initial_data['pk'] = pk_list
-            form = self.form(self.cls, parent_obj, initial=initial_data)
+            form = self.form(model, parent_obj, initial=initial_data)
 
         # Retrieve objects being edited
-        queryset = self.queryset or self.cls.objects.all()
-        table = self.table(queryset.filter(pk__in=pk_list), orderable=False)
+        table = self.table(self.queryset.filter(pk__in=pk_list), orderable=False)
         if not table.rows:
-            messages.warning(request, "No {} were selected.".format(self.cls._meta.verbose_name_plural))
+            messages.warning(request, "No {} were selected.".format(model._meta.verbose_name_plural))
             return redirect(self.get_return_url(request))
 
         return render(request, self.template_name, {
             'form': form,
             'table': table,
-            'obj_type_plural': self.cls._meta.verbose_name_plural,
+            'obj_type_plural': model._meta.verbose_name_plural,
             'return_url': self.get_return_url(request),
         })
 
@@ -584,17 +584,15 @@ class BulkDeleteView(GetReturnURLMixin, View):
     """
     Delete objects in bulk.
 
-    cls: The model of the objects being deleted
-    parent_cls: The model of the parent object (if any)
     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 deleted
     form: The form class used to delete objects in bulk
     template_name: The name of the template
     """
-    cls = None
-    parent_cls = None
     queryset = None
+    parent_model = None
     filter = None
     table = None
     form = None
@@ -605,18 +603,20 @@ class BulkDeleteView(GetReturnURLMixin, View):
 
     def post(self, request, **kwargs):
 
+        model = self.queryset.model
+
         # Attempt to derive parent object if a parent class has been given
-        if self.parent_cls:
-            parent_obj = get_object_or_404(self.parent_cls, **kwargs)
+        if self.parent_model:
+            parent_obj = get_object_or_404(self.parent_model, **kwargs)
         else:
             parent_obj = None
 
         # Are we deleting *all* objects in the queryset or just a selected subset?
         if request.POST.get('_all'):
             if self.filter is not None:
-                pk_list = [obj.pk for obj in self.filter(request.GET, self.cls.objects.only('pk')).qs]
+                pk_list = [obj.pk for obj in self.filter(request.GET, model.objects.only('pk')).qs]
             else:
-                pk_list = self.cls.objects.values_list('pk', flat=True)
+                pk_list = model.objects.values_list('pk', flat=True)
         else:
             pk_list = [int(pk) for pk in request.POST.getlist('pk')]
 
@@ -627,14 +627,14 @@ class BulkDeleteView(GetReturnURLMixin, View):
             if form.is_valid():
 
                 # Delete objects
-                queryset = self.cls.objects.filter(pk__in=pk_list)
+                queryset = model.objects.filter(pk__in=pk_list)
                 try:
-                    deleted_count = queryset.delete()[1][self.cls._meta.label]
+                    deleted_count = queryset.delete()[1][model._meta.label]
                 except ProtectedError as e:
                     handle_protectederror(list(queryset), request, e)
                     return redirect(self.get_return_url(request))
 
-                msg = 'Deleted {} {}'.format(deleted_count, self.cls._meta.verbose_name_plural)
+                msg = 'Deleted {} {}'.format(deleted_count, model._meta.verbose_name_plural)
                 messages.success(request, msg)
                 return redirect(self.get_return_url(request))
 
@@ -645,16 +645,15 @@ class BulkDeleteView(GetReturnURLMixin, View):
             })
 
         # Retrieve objects being deleted
-        queryset = self.queryset or self.cls.objects.all()
-        table = self.table(queryset.filter(pk__in=pk_list), orderable=False)
+        table = self.table(self.queryset.filter(pk__in=pk_list), orderable=False)
         if not table.rows:
-            messages.warning(request, "No {} were selected for deletion.".format(self.cls._meta.verbose_name_plural))
+            messages.warning(request, "No {} were selected for deletion.".format(model._meta.verbose_name_plural))
             return redirect(self.get_return_url(request))
 
         return render(request, self.template_name, {
             'form': form,
             'parent_obj': parent_obj,
-            'obj_type_plural': self.cls._meta.verbose_name_plural,
+            'obj_type_plural': model._meta.verbose_name_plural,
             'table': table,
             'return_url': self.get_return_url(request),
         })
@@ -665,7 +664,7 @@ class BulkDeleteView(GetReturnURLMixin, View):
         """
 
         class BulkDeleteForm(ConfirmationForm):
-            pk = ModelMultipleChoiceField(queryset=self.cls.objects.all(), widget=MultipleHiddenInput)
+            pk = ModelMultipleChoiceField(queryset=self.queryset, widget=MultipleHiddenInput)
 
         if self.form:
             return self.form

+ 10 - 15
netbox/virtualization/views.py

@@ -49,7 +49,6 @@ class ClusterTypeBulkImportView(PermissionRequiredMixin, BulkImportView):
 
 class ClusterTypeBulkDeleteView(PermissionRequiredMixin, BulkDeleteView):
     permission_required = 'virtualization.delete_clustertype'
-    cls = ClusterType
     queryset = ClusterType.objects.annotate(cluster_count=Count('clusters'))
     table = tables.ClusterTypeTable
     default_return_url = 'virtualization:clustertype_list'
@@ -85,7 +84,6 @@ class ClusterGroupBulkImportView(PermissionRequiredMixin, BulkImportView):
 
 class ClusterGroupBulkDeleteView(PermissionRequiredMixin, BulkDeleteView):
     permission_required = 'virtualization.delete_clustergroup'
-    cls = ClusterGroup
     queryset = ClusterGroup.objects.annotate(cluster_count=Count('clusters'))
     table = tables.ClusterGroupTable
     default_return_url = 'virtualization:clustergroup_list'
@@ -96,7 +94,7 @@ class ClusterGroupBulkDeleteView(PermissionRequiredMixin, BulkDeleteView):
 #
 
 class ClusterListView(ObjectListView):
-    queryset = Cluster.objects.select_related('type', 'group')
+    queryset = Cluster.objects.select_related('type', 'group', 'site')
     table = tables.ClusterTable
     filter = filters.ClusterFilter
     filter_form = forms.ClusterFilterForm
@@ -147,7 +145,7 @@ class ClusterBulkImportView(PermissionRequiredMixin, BulkImportView):
 
 class ClusterBulkEditView(PermissionRequiredMixin, BulkEditView):
     permission_required = 'virtualization.change_cluster'
-    cls = Cluster
+    queryset = Cluster.objects.select_related('type', 'group', 'site')
     filter = filters.ClusterFilter
     table = tables.ClusterTable
     form = forms.ClusterBulkEditForm
@@ -156,8 +154,7 @@ class ClusterBulkEditView(PermissionRequiredMixin, BulkEditView):
 
 class ClusterBulkDeleteView(PermissionRequiredMixin, BulkDeleteView):
     permission_required = 'virtualization.delete_cluster'
-    cls = Cluster
-    queryset = Cluster.objects.all()
+    queryset = Cluster.objects.select_related('type', 'group', 'site')
     filter = filters.ClusterFilter
     table = tables.ClusterTable
     default_return_url = 'virtualization:cluster_list'
@@ -244,7 +241,7 @@ class ClusterRemoveDevicesView(PermissionRequiredMixin, View):
 #
 
 class VirtualMachineListView(ObjectListView):
-    queryset = VirtualMachine.objects.select_related('cluster', 'tenant', 'primary_ip4', 'primary_ip6')
+    queryset = VirtualMachine.objects.select_related('cluster', 'tenant', 'role', 'primary_ip4', 'primary_ip6')
     filter = filters.VirtualMachineFilter
     filter_form = forms.VirtualMachineFilterForm
     table = tables.VirtualMachineDetailTable
@@ -298,8 +295,7 @@ class VirtualMachineBulkImportView(PermissionRequiredMixin, BulkImportView):
 
 class VirtualMachineBulkEditView(PermissionRequiredMixin, BulkEditView):
     permission_required = 'virtualization.change_virtualmachine'
-    cls = VirtualMachine
-    queryset = VirtualMachine.objects.select_related('cluster', 'tenant')
+    queryset = VirtualMachine.objects.select_related('cluster', 'tenant', 'role')
     filter = filters.VirtualMachineFilter
     table = tables.VirtualMachineTable
     form = forms.VirtualMachineBulkEditForm
@@ -308,8 +304,7 @@ class VirtualMachineBulkEditView(PermissionRequiredMixin, BulkEditView):
 
 class VirtualMachineBulkDeleteView(PermissionRequiredMixin, BulkDeleteView):
     permission_required = 'virtualization.delete_virtualmachine'
-    cls = VirtualMachine
-    queryset = VirtualMachine.objects.select_related('cluster', 'tenant')
+    queryset = VirtualMachine.objects.select_related('cluster', 'tenant', 'role')
     filter = filters.VirtualMachineFilter
     table = tables.VirtualMachineTable
     default_return_url = 'virtualization:virtualmachine_list'
@@ -343,16 +338,16 @@ class InterfaceDeleteView(PermissionRequiredMixin, ObjectDeleteView):
 
 class InterfaceBulkEditView(PermissionRequiredMixin, BulkEditView):
     permission_required = 'dcim.change_interface'
-    cls = Interface
-    parent_cls = VirtualMachine
+    queryset = Interface.objects.all()
+    parent_model = VirtualMachine
     table = tables.InterfaceTable
     form = forms.InterfaceBulkEditForm
 
 
 class InterfaceBulkDeleteView(PermissionRequiredMixin, BulkDeleteView):
     permission_required = 'dcim.delete_interface'
-    cls = Interface
-    parent_cls = VirtualMachine
+    queryset = Interface.objects.all()
+    parent_model = VirtualMachine
     table = tables.InterfaceTable