diff --git a/docs/plugins/development/views.md b/docs/plugins/development/views.md index d5d376db8..01c7737ba 100644 --- a/docs/plugins/development/views.md +++ b/docs/plugins/development/views.md @@ -64,6 +64,7 @@ Generic view classes (documented below) facilitate common operations, such as cr | `ObjectListView` | View a list of objects | | `BulkImportView` | Import a set of new objects | | `BulkEditView` | Edit multiple objects | +| `BulkRenameView` | Rename multiple objects | | `BulkDeleteView` | Delete multiple objects | !!! warning @@ -171,6 +172,10 @@ Below are the class definitions for NetBox's multi-object views. These views han options: members: false +::: netbox.views.generic.BulkRenameView + options: + members: false + ::: netbox.views.generic.BulkDeleteView options: members: diff --git a/netbox/circuits/views.py b/netbox/circuits/views.py index 0b4439857..8670b8535 100644 --- a/netbox/circuits/views.py +++ b/netbox/circuits/views.py @@ -5,6 +5,7 @@ from django.utils.translation import gettext_lazy as _ from dcim.views import PathTraceView from ipam.models import ASN +from netbox.object_actions import AddObject, BulkDelete, BulkEdit, BulkExport, BulkImport from netbox.views import generic from utilities.forms import ConfirmationForm from utilities.query import count_related @@ -79,6 +80,11 @@ class ProviderBulkEditView(generic.BulkEditView): form = forms.ProviderBulkEditForm +@register_model_view(Provider, 'bulk_rename', path='rename', detail=False) +class ProviderBulkRenameView(generic.BulkRenameView): + queryset = Provider.objects.all() + + @register_model_view(Provider, 'bulk_delete', path='delete', detail=False) class ProviderBulkDeleteView(generic.BulkDeleteView): queryset = Provider.objects.annotate( @@ -141,6 +147,11 @@ class ProviderAccountBulkEditView(generic.BulkEditView): form = forms.ProviderAccountBulkEditForm +@register_model_view(ProviderAccount, 'bulk_rename', path='rename', detail=False) +class ProviderAccountBulkRenameView(generic.BulkRenameView): + queryset = ProviderAccount.objects.all() + + @register_model_view(ProviderAccount, 'bulk_delete', path='delete', detail=False) class ProviderAccountBulkDeleteView(generic.BulkDeleteView): queryset = ProviderAccount.objects.annotate( @@ -212,6 +223,11 @@ class ProviderNetworkBulkEditView(generic.BulkEditView): form = forms.ProviderNetworkBulkEditForm +@register_model_view(ProviderNetwork, 'bulk_rename', path='rename', detail=False) +class ProviderNetworkBulkRenameView(generic.BulkRenameView): + queryset = ProviderNetwork.objects.all() + + @register_model_view(ProviderNetwork, 'bulk_delete', path='delete', detail=False) class ProviderNetworkBulkDeleteView(generic.BulkDeleteView): queryset = ProviderNetwork.objects.all() @@ -271,6 +287,11 @@ class CircuitTypeBulkEditView(generic.BulkEditView): form = forms.CircuitTypeBulkEditForm +@register_model_view(CircuitType, 'bulk_rename', path='rename', detail=False) +class CircuitTypeBulkRenameView(generic.BulkRenameView): + queryset = CircuitType.objects.all() + + @register_model_view(CircuitType, 'bulk_delete', path='delete', detail=False) class CircuitTypeBulkDeleteView(generic.BulkDeleteView): queryset = CircuitType.objects.annotate( @@ -337,6 +358,12 @@ class CircuitBulkEditView(generic.BulkEditView): form = forms.CircuitBulkEditForm +@register_model_view(Circuit, 'bulk_rename', path='rename', detail=False) +class CircuitBulkRenameView(generic.BulkRenameView): + queryset = Circuit.objects.all() + field_name = 'cid' + + @register_model_view(Circuit, 'bulk_delete', path='delete', detail=False) class CircuitBulkDeleteView(generic.BulkDeleteView): queryset = Circuit.objects.prefetch_related( @@ -432,6 +459,7 @@ class CircuitTerminationListView(generic.ObjectListView): filterset = filtersets.CircuitTerminationFilterSet filterset_form = forms.CircuitTerminationFilterForm table = tables.CircuitTerminationTable + actions = (AddObject, BulkImport, BulkExport, BulkEdit, BulkDelete) @register_model_view(CircuitTermination) @@ -526,6 +554,11 @@ class CircuitGroupBulkEditView(generic.BulkEditView): form = forms.CircuitGroupBulkEditForm +@register_model_view(CircuitGroup, 'bulk_rename', path='rename', detail=False) +class CircuitGroupBulkRenameView(generic.BulkRenameView): + queryset = CircuitGroup.objects.all() + + @register_model_view(CircuitGroup, 'bulk_delete', path='delete', detail=False) class CircuitGroupBulkDeleteView(generic.BulkDeleteView): queryset = CircuitGroup.objects.all() @@ -543,6 +576,7 @@ class CircuitGroupAssignmentListView(generic.ObjectListView): filterset = filtersets.CircuitGroupAssignmentFilterSet filterset_form = forms.CircuitGroupAssignmentFilterForm table = tables.CircuitGroupAssignmentTable + actions = (AddObject, BulkImport, BulkExport, BulkEdit, BulkDelete) @register_model_view(CircuitGroupAssignment) @@ -635,6 +669,11 @@ class VirtualCircuitTypeBulkEditView(generic.BulkEditView): form = forms.VirtualCircuitTypeBulkEditForm +@register_model_view(VirtualCircuitType, 'bulk_rename', path='rename', detail=False) +class VirtualCircuitTypeBulkRenameView(generic.BulkRenameView): + queryset = VirtualCircuitType.objects.all() + + @register_model_view(VirtualCircuitType, 'bulk_delete', path='delete', detail=False) class VirtualCircuitTypeBulkDeleteView(generic.BulkDeleteView): queryset = VirtualCircuitType.objects.annotate( @@ -697,6 +736,12 @@ class VirtualCircuitBulkEditView(generic.BulkEditView): form = forms.VirtualCircuitBulkEditForm +@register_model_view(VirtualCircuit, 'bulk_rename', path='rename', detail=False) +class VirtualCircuitulkRenameView(generic.BulkRenameView): + queryset = VirtualCircuit.objects.all() + field_name = 'cid' + + class VirtualCircuitBulkDeleteView(generic.BulkDeleteView): queryset = VirtualCircuit.objects.annotate( termination_count=count_related(VirtualCircuitTermination, 'virtual_circuit') @@ -714,6 +759,7 @@ class VirtualCircuitTerminationListView(generic.ObjectListView): filterset = filtersets.VirtualCircuitTerminationFilterSet filterset_form = forms.VirtualCircuitTerminationFilterForm table = tables.VirtualCircuitTerminationTable + actions = (AddObject, BulkImport, BulkExport, BulkEdit, BulkDelete) @register_model_view(VirtualCircuitTermination) diff --git a/netbox/core/views.py b/netbox/core/views.py index 1bd952b62..5729e5f2c 100644 --- a/netbox/core/views.py +++ b/netbox/core/views.py @@ -120,6 +120,11 @@ class DataSourceBulkEditView(generic.BulkEditView): form = forms.DataSourceBulkEditForm +@register_model_view(DataSource, 'bulk_rename', path='rename', detail=False) +class DataSourceBulkRenameView(generic.BulkRenameView): + queryset = DataSource.objects.all() + + @register_model_view(DataSource, 'bulk_delete', path='delete', detail=False) class DataSourceBulkDeleteView(generic.BulkDeleteView): queryset = DataSource.objects.annotate( diff --git a/netbox/dcim/views.py b/netbox/dcim/views.py index 6d72f5c43..94afc2cb2 100644 --- a/netbox/dcim/views.py +++ b/netbox/dcim/views.py @@ -292,6 +292,11 @@ class RegionBulkEditView(generic.BulkEditView): form = forms.RegionBulkEditForm +@register_model_view(Region, 'bulk_rename', path='rename', detail=False) +class RegionBulkRenameView(generic.BulkRenameView): + queryset = Region.objects.all() + + @register_model_view(Region, 'bulk_delete', path='delete', detail=False) class RegionBulkDeleteView(generic.BulkDeleteView): queryset = Region.objects.add_related_count( @@ -418,6 +423,11 @@ class SiteGroupBulkEditView(generic.BulkEditView): form = forms.SiteGroupBulkEditForm +@register_model_view(SiteGroup, 'bulk_rename', path='rename', detail=False) +class SiteGroupBulkRenameView(generic.BulkRenameView): + queryset = SiteGroup.objects.all() + + @register_model_view(SiteGroup, 'bulk_delete', path='delete', detail=False) class SiteGroupBulkDeleteView(generic.BulkDeleteView): queryset = SiteGroup.objects.add_related_count( @@ -503,6 +513,11 @@ class SiteBulkEditView(generic.BulkEditView): form = forms.SiteBulkEditForm +@register_model_view(Site, 'bulk_rename', path='rename', detail=False) +class SiteBulkRenameView(generic.BulkRenameView): + queryset = Site.objects.all() + + @register_model_view(Site, 'bulk_delete', path='delete', detail=False) class SiteBulkDeleteView(generic.BulkDeleteView): queryset = Site.objects.all() @@ -607,6 +622,11 @@ class LocationBulkEditView(generic.BulkEditView): form = forms.LocationBulkEditForm +@register_model_view(Location, 'bulk_rename', path='rename', detail=False) +class LocationBulkRenameView(generic.BulkRenameView): + queryset = Location.objects.all() + + @register_model_view(Location, 'bulk_delete', path='delete', detail=False) class LocationBulkDeleteView(generic.BulkDeleteView): queryset = Location.objects.add_related_count( @@ -672,6 +692,11 @@ class RackRoleBulkEditView(generic.BulkEditView): form = forms.RackRoleBulkEditForm +@register_model_view(RackRole, 'bulk_rename', path='rename', detail=False) +class RackRoleBulkRenameView(generic.BulkRenameView): + queryset = RackRole.objects.all() + + @register_model_view(RackRole, 'bulk_delete', path='delete', detail=False) class RackRoleBulkDeleteView(generic.BulkDeleteView): queryset = RackRole.objects.annotate( @@ -731,6 +756,12 @@ class RackTypeBulkEditView(generic.BulkEditView): form = forms.RackTypeBulkEditForm +@register_model_view(RackType, 'bulk_rename', path='rename', detail=False) +class RackTypeBulkRenameView(generic.BulkRenameView): + queryset = RackType.objects.all() + field_name = 'model' + + @register_model_view(RackType, 'bulk_delete', path='delete', detail=False) class RackTypeBulkDeleteView(generic.BulkDeleteView): queryset = RackType.objects.all() @@ -910,6 +941,11 @@ class RackBulkEditView(generic.BulkEditView): form = forms.RackBulkEditForm +@register_model_view(Rack, 'bulk_rename', path='rename', detail=False) +class RackBulkRenameView(generic.BulkRenameView): + queryset = Rack.objects.all() + + @register_model_view(Rack, 'bulk_delete', path='delete', detail=False) class RackBulkDeleteView(generic.BulkDeleteView): queryset = Rack.objects.all() @@ -927,6 +963,7 @@ class RackReservationListView(generic.ObjectListView): filterset = filtersets.RackReservationFilterSet filterset_form = forms.RackReservationFilterForm table = tables.RackReservationTable + actions = (AddObject, BulkImport, BulkExport, BulkEdit, BulkDelete) @register_model_view(RackReservation) @@ -1043,6 +1080,11 @@ class ManufacturerBulkEditView(generic.BulkEditView): form = forms.ManufacturerBulkEditForm +@register_model_view(Manufacturer, 'bulk_rename', path='rename', detail=False) +class ManufacturerBulkRenameView(generic.BulkRenameView): + queryset = Manufacturer.objects.all() + + @register_model_view(Manufacturer, 'bulk_delete', path='delete', detail=False) class ManufacturerBulkDeleteView(generic.BulkDeleteView): queryset = Manufacturer.objects.annotate( @@ -1290,6 +1332,12 @@ class DeviceTypeBulkEditView(generic.BulkEditView): form = forms.DeviceTypeBulkEditForm +@register_model_view(DeviceType, 'bulk_rename', path='rename', detail=False) +class DeviceTypeBulkRenameView(generic.BulkRenameView): + queryset = DeviceType.objects.all() + field_name = 'model' + + @register_model_view(DeviceType, 'bulk_delete', path='delete', detail=False) class DeviceTypeBulkDeleteView(generic.BulkDeleteView): queryset = DeviceType.objects.annotate( @@ -1346,6 +1394,11 @@ class ModuleTypeProfileBulkEditView(generic.BulkEditView): form = forms.ModuleTypeProfileBulkEditForm +@register_model_view(ModuleTypeProfile, 'bulk_rename', path='rename', detail=False) +class ModuleTypeProfileBulkRenameView(generic.BulkRenameView): + queryset = ModuleTypeProfile.objects.all() + + @register_model_view(ModuleTypeProfile, 'bulk_delete', path='delete', detail=False) class ModuleTypeProfileBulkDeleteView(generic.BulkDeleteView): queryset = ModuleTypeProfile.objects.annotate( @@ -1556,6 +1609,11 @@ class ModuleTypeBulkEditView(generic.BulkEditView): form = forms.ModuleTypeBulkEditForm +@register_model_view(ModuleType, 'bulk_rename', path='rename', detail=False) +class ModuleTypeBulkRenameView(generic.BulkRenameView): + queryset = ModuleType.objects.all() + + @register_model_view(ModuleType, 'bulk_delete', path='delete', detail=False) class ModuleTypeBulkDeleteView(generic.BulkDeleteView): queryset = ModuleType.objects.annotate( @@ -2030,6 +2088,11 @@ class DeviceRoleBulkEditView(generic.BulkEditView): form = forms.DeviceRoleBulkEditForm +@register_model_view(DeviceRole, 'bulk_rename', path='rename', detail=False) +class DeviceRoleBulkRenameView(generic.BulkRenameView): + queryset = DeviceRole.objects.all() + + @register_model_view(DeviceRole, 'bulk_delete', path='delete', detail=False) class DeviceRoleBulkDeleteView(generic.BulkDeleteView): queryset = DeviceRole.objects.annotate( @@ -2091,6 +2154,11 @@ class PlatformBulkEditView(generic.BulkEditView): form = forms.PlatformBulkEditForm +@register_model_view(Platform, 'bulk_rename', path='rename', detail=False) +class PlatformBulkRenameView(generic.BulkRenameView): + queryset = Platform.objects.all() + + @register_model_view(Platform, 'bulk_delete', path='delete', detail=False) class PlatformBulkDeleteView(generic.BulkDeleteView): queryset = Platform.objects.all() @@ -2374,16 +2442,16 @@ class DeviceBulkEditView(generic.BulkEditView): form = forms.DeviceBulkEditForm -@register_model_view(Device, 'bulk_delete', path='delete', detail=False) -class DeviceBulkDeleteView(generic.BulkDeleteView): - queryset = Device.objects.prefetch_related('device_type__manufacturer') +@register_model_view(Device, 'bulk_rename', path='rename', detail=False) +class DeviceBulkRenameView(generic.BulkRenameView): + queryset = Device.objects.all() filterset = filtersets.DeviceFilterSet table = tables.DeviceTable -@register_model_view(Device, 'bulk_rename', path='rename', detail=False) -class DeviceBulkRenameView(generic.BulkRenameView): - queryset = Device.objects.all() +@register_model_view(Device, 'bulk_delete', path='delete', detail=False) +class DeviceBulkDeleteView(generic.BulkDeleteView): + queryset = Device.objects.prefetch_related('device_type__manufacturer') filterset = filtersets.DeviceFilterSet table = tables.DeviceTable @@ -2398,6 +2466,7 @@ class ModuleListView(generic.ObjectListView): filterset = filtersets.ModuleFilterSet filterset_form = forms.ModuleFilterForm table = tables.ModuleTable + actions = (AddObject, BulkImport, BulkExport, BulkEdit, BulkDelete) @register_model_view(Module) @@ -2453,7 +2522,6 @@ class ConsolePortListView(generic.ObjectListView): filterset = filtersets.ConsolePortFilterSet filterset_form = forms.ConsolePortFilterForm table = tables.ConsolePortTable - actions = (AddObject, BulkImport, BulkEdit, BulkRename, BulkExport, BulkDelete) @register_model_view(ConsolePort) @@ -2524,7 +2592,6 @@ class ConsoleServerPortListView(generic.ObjectListView): filterset = filtersets.ConsoleServerPortFilterSet filterset_form = forms.ConsoleServerPortFilterForm table = tables.ConsoleServerPortTable - actions = (AddObject, BulkImport, BulkEdit, BulkRename, BulkExport, BulkDelete) @register_model_view(ConsoleServerPort) @@ -2595,7 +2662,6 @@ class PowerPortListView(generic.ObjectListView): filterset = filtersets.PowerPortFilterSet filterset_form = forms.PowerPortFilterForm table = tables.PowerPortTable - actions = (AddObject, BulkImport, BulkEdit, BulkRename, BulkExport, BulkDelete) @register_model_view(PowerPort) @@ -2666,7 +2732,6 @@ class PowerOutletListView(generic.ObjectListView): filterset = filtersets.PowerOutletFilterSet filterset_form = forms.PowerOutletFilterForm table = tables.PowerOutletTable - actions = (AddObject, BulkImport, BulkEdit, BulkRename, BulkExport, BulkDelete) @register_model_view(PowerOutlet) @@ -2737,7 +2802,6 @@ class InterfaceListView(generic.ObjectListView): filterset = filtersets.InterfaceFilterSet filterset_form = forms.InterfaceFilterForm table = tables.InterfaceTable - actions = (AddObject, BulkImport, BulkEdit, BulkRename, BulkExport, BulkDelete) @register_model_view(Interface) @@ -2881,7 +2945,6 @@ class FrontPortListView(generic.ObjectListView): filterset = filtersets.FrontPortFilterSet filterset_form = forms.FrontPortFilterForm table = tables.FrontPortTable - actions = (AddObject, BulkImport, BulkEdit, BulkRename, BulkExport, BulkDelete) @register_model_view(FrontPort) @@ -2952,7 +3015,6 @@ class RearPortListView(generic.ObjectListView): filterset = filtersets.RearPortFilterSet filterset_form = forms.RearPortFilterForm table = tables.RearPortTable - actions = (AddObject, BulkImport, BulkEdit, BulkRename, BulkExport, BulkDelete) @register_model_view(RearPort) @@ -3023,7 +3085,6 @@ class ModuleBayListView(generic.ObjectListView): filterset = filtersets.ModuleBayFilterSet filterset_form = forms.ModuleBayFilterForm table = tables.ModuleBayTable - actions = (AddObject, BulkImport, BulkEdit, BulkRename, BulkExport, BulkDelete) @register_model_view(ModuleBay) @@ -3085,7 +3146,6 @@ class DeviceBayListView(generic.ObjectListView): filterset = filtersets.DeviceBayFilterSet filterset_form = forms.DeviceBayFilterForm table = tables.DeviceBayTable - actions = (AddObject, BulkImport, BulkEdit, BulkRename, BulkExport, BulkDelete) @register_model_view(DeviceBay) @@ -3228,7 +3288,6 @@ class InventoryItemListView(generic.ObjectListView): filterset = filtersets.InventoryItemFilterSet filterset_form = forms.InventoryItemFilterForm table = tables.InventoryItemTable - actions = (AddObject, BulkImport, BulkEdit, BulkRename, BulkExport, BulkDelete) @register_model_view(InventoryItem) @@ -3351,6 +3410,11 @@ class InventoryItemRoleBulkEditView(generic.BulkEditView): form = forms.InventoryItemRoleBulkEditForm +@register_model_view(InventoryItemRole, 'bulk_rename', path='rename', detail=False) +class InventoryItemRoleBulkRenameView(generic.BulkRenameView): + queryset = InventoryItemRole.objects.all() + + @register_model_view(InventoryItemRole, 'bulk_delete', path='delete', detail=False) class InventoryItemRoleBulkDeleteView(generic.BulkDeleteView): queryset = InventoryItemRole.objects.annotate( @@ -3548,6 +3612,12 @@ class CableBulkEditView(generic.BulkEditView): form = forms.CableBulkEditForm +@register_model_view(Cable, 'bulk_rename', path='rename', detail=False) +class CableBulkRenameView(generic.BulkRenameView): + queryset = Cable.objects.all() + field_name = 'label' + + @register_model_view(Cable, 'bulk_delete', path='delete', detail=False) class CableBulkDeleteView(generic.BulkDeleteView): queryset = Cable.objects.prefetch_related( @@ -3840,6 +3910,11 @@ class VirtualChassisBulkEditView(generic.BulkEditView): form = forms.VirtualChassisBulkEditForm +@register_model_view(VirtualChassis, 'bulk_rename', path='rename', detail=False) +class VirtualChassisBulkRenameView(generic.BulkRenameView): + queryset = VirtualChassis.objects.all() + + @register_model_view(VirtualChassis, 'bulk_delete', path='delete', detail=False) class VirtualChassisBulkDeleteView(generic.BulkDeleteView): queryset = VirtualChassis.objects.all() @@ -3897,6 +3972,11 @@ class PowerPanelBulkEditView(generic.BulkEditView): form = forms.PowerPanelBulkEditForm +@register_model_view(PowerPanel, 'bulk_rename', path='rename', detail=False) +class PowerPanelBulkRenameView(generic.BulkRenameView): + queryset = PowerPanel.objects.all() + + @register_model_view(PowerPanel, 'bulk_delete', path='delete', detail=False) class PowerPanelBulkDeleteView(generic.BulkDeleteView): queryset = PowerPanel.objects.annotate( @@ -3949,6 +4029,11 @@ class PowerFeedBulkEditView(generic.BulkEditView): form = forms.PowerFeedBulkEditForm +@register_model_view(PowerFeed, 'bulk_rename', path='rename', detail=False) +class PowerFeedBulkRenameView(generic.BulkRenameView): + queryset = PowerFeed.objects.all() + + @register_model_view(PowerFeed, 'bulk_disconnect', path='disconnect', detail=False) class PowerFeedBulkDisconnectView(BulkDisconnectView): queryset = PowerFeed.objects.all() @@ -3977,6 +4062,7 @@ class VirtualDeviceContextListView(generic.ObjectListView): filterset = filtersets.VirtualDeviceContextFilterSet filterset_form = forms.VirtualDeviceContextFilterForm table = tables.VirtualDeviceContextTable + actions = (AddObject, BulkImport, BulkEdit, BulkRename, BulkExport, BulkDelete) @register_model_view(VirtualDeviceContext) @@ -4021,6 +4107,11 @@ class VirtualDeviceContextBulkEditView(generic.BulkEditView): form = forms.VirtualDeviceContextBulkEditForm +@register_model_view(VirtualDeviceContext, 'bulk_rename', path='rename', detail=False) +class VirtualDeviceContextBulkRenameView(generic.BulkRenameView): + queryset = VirtualDeviceContext.objects.all() + + @register_model_view(VirtualDeviceContext, 'bulk_delete', path='delete', detail=False) class VirtualDeviceContextBulkDeleteView(generic.BulkDeleteView): queryset = VirtualDeviceContext.objects.all() @@ -4038,6 +4129,7 @@ class MACAddressListView(generic.ObjectListView): filterset = filtersets.MACAddressFilterSet filterset_form = forms.MACAddressFilterForm table = tables.MACAddressTable + actions = (AddObject, BulkImport, BulkExport, BulkEdit, BulkDelete) @register_model_view(MACAddress) diff --git a/netbox/extras/views.py b/netbox/extras/views.py index 7216c4eec..43172139c 100644 --- a/netbox/extras/views.py +++ b/netbox/extras/views.py @@ -97,6 +97,11 @@ class CustomFieldBulkEditView(generic.BulkEditView): form = forms.CustomFieldBulkEditForm +@register_model_view(CustomField, 'bulk_rename', path='rename', detail=False) +class CustomFieldBulkRenameView(generic.BulkRenameView): + queryset = CustomField.objects.all() + + @register_model_view(CustomField, 'bulk_delete', path='delete', detail=False) class CustomFieldBulkDeleteView(generic.BulkDeleteView): queryset = CustomField.objects.select_related('choice_set') @@ -166,6 +171,11 @@ class CustomFieldChoiceSetBulkEditView(generic.BulkEditView): form = forms.CustomFieldChoiceSetBulkEditForm +@register_model_view(CustomFieldChoiceSet, 'bulk_rename', path='rename', detail=False) +class CustomFieldChoiceSetBulkRenameView(generic.BulkRenameView): + queryset = CustomFieldChoiceSet.objects.all() + + @register_model_view(CustomFieldChoiceSet, 'bulk_delete', path='delete', detail=False) class CustomFieldChoiceSetBulkDeleteView(generic.BulkDeleteView): queryset = CustomFieldChoiceSet.objects.all() @@ -216,6 +226,11 @@ class CustomLinkBulkEditView(generic.BulkEditView): form = forms.CustomLinkBulkEditForm +@register_model_view(CustomLink, 'bulk_rename', path='rename', detail=False) +class CustomLinkBulkRenameView(generic.BulkRenameView): + queryset = CustomLink.objects.all() + + @register_model_view(CustomLink, 'bulk_delete', path='delete', detail=False) class CustomLinkBulkDeleteView(generic.BulkDeleteView): queryset = CustomLink.objects.all() @@ -233,7 +248,7 @@ class ExportTemplateListView(generic.ObjectListView): filterset = filtersets.ExportTemplateFilterSet filterset_form = forms.ExportTemplateFilterForm table = tables.ExportTemplateTable - actions = (AddObject, BulkImport, BulkSync, BulkEdit, BulkExport, BulkDelete) + actions = (AddObject, BulkImport, BulkSync, BulkExport, BulkEdit, BulkRename, BulkDelete) @register_model_view(ExportTemplate) @@ -267,6 +282,11 @@ class ExportTemplateBulkEditView(generic.BulkEditView): form = forms.ExportTemplateBulkEditForm +@register_model_view(ExportTemplate, 'bulk_rename', path='rename', detail=False) +class ExportTemplateBulkRenameView(generic.BulkRenameView): + queryset = ExportTemplate.objects.all() + + @register_model_view(ExportTemplate, 'bulk_delete', path='delete', detail=False) class ExportTemplateBulkDeleteView(generic.BulkDeleteView): queryset = ExportTemplate.objects.all() @@ -327,6 +347,11 @@ class SavedFilterBulkEditView(SharedObjectViewMixin, generic.BulkEditView): form = forms.SavedFilterBulkEditForm +@register_model_view(SavedFilter, 'bulk_rename', path='rename', detail=False) +class SavedFilterBulkRenameView(generic.BulkRenameView): + queryset = SavedFilter.objects.all() + + @register_model_view(SavedFilter, 'bulk_delete', path='delete', detail=False) class SavedFilterBulkDeleteView(SharedObjectViewMixin, generic.BulkDeleteView): queryset = SavedFilter.objects.all() @@ -344,7 +369,7 @@ class TableConfigListView(SharedObjectViewMixin, generic.ObjectListView): filterset = filtersets.TableConfigFilterSet filterset_form = forms.TableConfigFilterForm table = tables.TableConfigTable - actions = (BulkExport,) + actions = (BulkExport, BulkEdit, BulkRename, BulkDelete) @register_model_view(TableConfig) @@ -384,6 +409,11 @@ class TableConfigBulkEditView(SharedObjectViewMixin, generic.BulkEditView): form = forms.TableConfigBulkEditForm +@register_model_view(TableConfig, 'bulk_rename', path='rename', detail=False) +class TableConfigBulkRenameView(generic.BulkRenameView): + queryset = TableConfig.objects.all() + + @register_model_view(TableConfig, 'bulk_delete', path='delete', detail=False) class TableConfigBulkDeleteView(SharedObjectViewMixin, generic.BulkDeleteView): queryset = TableConfig.objects.all() @@ -465,6 +495,11 @@ class NotificationGroupBulkEditView(generic.BulkEditView): form = forms.NotificationGroupBulkEditForm +@register_model_view(NotificationGroup, 'bulk_rename', path='rename', detail=False) +class NotificationGroupBulkRenameView(generic.BulkRenameView): + queryset = NotificationGroup.objects.all() + + @register_model_view(NotificationGroup, 'bulk_delete', path='delete', detail=False) class NotificationGroupBulkDeleteView(generic.BulkDeleteView): queryset = NotificationGroup.objects.all() @@ -611,6 +646,11 @@ class WebhookBulkEditView(generic.BulkEditView): form = forms.WebhookBulkEditForm +@register_model_view(Webhook, 'bulk_rename', path='rename', detail=False) +class WebhookBulkRenameView(generic.BulkRenameView): + queryset = Webhook.objects.all() + + @register_model_view(Webhook, 'bulk_delete', path='delete', detail=False) class WebhookBulkDeleteView(generic.BulkDeleteView): queryset = Webhook.objects.all() @@ -661,6 +701,11 @@ class EventRuleBulkEditView(generic.BulkEditView): form = forms.EventRuleBulkEditForm +@register_model_view(EventRule, 'bulk_rename', path='rename', detail=False) +class EventRuleBulkRenameView(generic.BulkRenameView): + queryset = EventRule.objects.all() + + @register_model_view(EventRule, 'bulk_delete', path='delete', detail=False) class EventRuleBulkDeleteView(generic.BulkDeleteView): queryset = EventRule.objects.all() @@ -735,6 +780,11 @@ class TagBulkEditView(generic.BulkEditView): form = forms.TagBulkEditForm +@register_model_view(Tag, 'bulk_rename', path='rename', detail=False) +class TagBulkRenameView(generic.BulkRenameView): + queryset = Tag.objects.all() + + @register_model_view(Tag, 'bulk_delete', path='delete', detail=False) class TagBulkDeleteView(generic.BulkDeleteView): queryset = Tag.objects.annotate( @@ -753,7 +803,7 @@ class ConfigContextListView(generic.ObjectListView): filterset = filtersets.ConfigContextFilterSet filterset_form = forms.ConfigContextFilterForm table = tables.ConfigContextTable - actions = (AddObject, BulkSync, BulkEdit, BulkDelete) + actions = (AddObject, BulkSync, BulkEdit, BulkRename, BulkDelete) @register_model_view(ConfigContext) @@ -814,6 +864,11 @@ class ConfigContextBulkEditView(generic.BulkEditView): form = forms.ConfigContextBulkEditForm +@register_model_view(ConfigContext, 'bulk_rename', path='rename', detail=False) +class ConfigContextBulkRenameView(generic.BulkRenameView): + queryset = ConfigContext.objects.all() + + @register_model_view(ConfigContext, 'bulk_delete', path='delete', detail=False) class ConfigContextBulkDeleteView(generic.BulkDeleteView): queryset = ConfigContext.objects.all() @@ -866,7 +921,7 @@ class ConfigTemplateListView(generic.ObjectListView): filterset = filtersets.ConfigTemplateFilterSet filterset_form = forms.ConfigTemplateFilterForm table = tables.ConfigTemplateTable - actions = (AddObject, BulkImport, BulkSync, BulkEdit, BulkExport, BulkDelete) + actions = (AddObject, BulkImport, BulkExport, BulkSync, BulkEdit, BulkRename, BulkDelete) @register_model_view(ConfigTemplate) @@ -900,6 +955,11 @@ class ConfigTemplateBulkEditView(generic.BulkEditView): form = forms.ConfigTemplateBulkEditForm +@register_model_view(ConfigTemplate, 'bulk_rename', path='rename', detail=False) +class ConfigTemplateBulkRenameView(generic.BulkRenameView): + queryset = ConfigTemplate.objects.all() + + @register_model_view(ConfigTemplate, 'bulk_delete', path='delete', detail=False) class ConfigTemplateBulkDeleteView(generic.BulkDeleteView): queryset = ConfigTemplate.objects.all() diff --git a/netbox/ipam/views.py b/netbox/ipam/views.py index 8efa06191..61a5ce563 100644 --- a/netbox/ipam/views.py +++ b/netbox/ipam/views.py @@ -10,6 +10,7 @@ from dcim.filtersets import InterfaceFilterSet from dcim.forms import InterfaceFilterForm from dcim.models import Device, Interface, Site from ipam.tables import VLANTranslationRuleTable +from netbox.object_actions import AddObject, BulkDelete, BulkEdit, BulkExport, BulkImport from netbox.views import generic from utilities.query import count_related from utilities.tables import get_table_ordering @@ -86,6 +87,11 @@ class VRFBulkEditView(generic.BulkEditView): form = forms.VRFBulkEditForm +@register_model_view(VRF, 'bulk_rename', path='rename', detail=False) +class VRFBulkRenameView(generic.BulkRenameView): + queryset = VRF.objects.all() + + @register_model_view(VRF, 'bulk_delete', path='delete', detail=False) class VRFBulkDeleteView(generic.BulkDeleteView): queryset = VRF.objects.all() @@ -136,6 +142,11 @@ class RouteTargetBulkEditView(generic.BulkEditView): form = forms.RouteTargetBulkEditForm +@register_model_view(RouteTarget, 'bulk_rename', path='rename', detail=False) +class RouteTargetBulkRenameView(generic.BulkRenameView): + queryset = RouteTarget.objects.all() + + @register_model_view(RouteTarget, 'bulk_delete', path='delete', detail=False) class RouteTargetBulkDeleteView(generic.BulkDeleteView): queryset = RouteTarget.objects.all() @@ -195,6 +206,11 @@ class RIRBulkEditView(generic.BulkEditView): form = forms.RIRBulkEditForm +@register_model_view(RIR, 'bulk_rename', path='rename', detail=False) +class RIRBulkRenameView(generic.BulkRenameView): + queryset = RIR.objects.all() + + @register_model_view(RIR, 'bulk_delete', path='delete', detail=False) class RIRBulkDeleteView(generic.BulkDeleteView): queryset = RIR.objects.annotate( @@ -268,6 +284,11 @@ class ASNRangeBulkEditView(generic.BulkEditView): form = forms.ASNRangeBulkEditForm +@register_model_view(ASNRange, 'bulk_rename', path='rename', detail=False) +class ASNRangeBulkRenameView(generic.BulkRenameView): + queryset = ASNRange.objects.all() + + @register_model_view(ASNRange, 'bulk_delete', path='delete', detail=False) class ASNRangeBulkDeleteView(generic.BulkDeleteView): queryset = ASNRange.objects.annotate_asn_counts() @@ -335,6 +356,11 @@ class ASNBulkEditView(generic.BulkEditView): form = forms.ASNBulkEditForm +@register_model_view(ASN, 'bulk_rename', path='rename', detail=False) +class ASNBulkRenameView(generic.BulkRenameView): + queryset = ASN.objects.all() + + @register_model_view(ASN, 'bulk_delete', path='delete', detail=False) class ASNBulkDeleteView(generic.BulkDeleteView): queryset = ASN.objects.annotate( @@ -356,6 +382,7 @@ class AggregateListView(generic.ObjectListView): filterset = filtersets.AggregateFilterSet filterset_form = forms.AggregateFilterForm table = tables.AggregateTable + actions = (AddObject, BulkImport, BulkExport, BulkEdit, BulkDelete) @register_model_view(Aggregate) @@ -488,6 +515,11 @@ class RoleBulkEditView(generic.BulkEditView): form = forms.RoleBulkEditForm +@register_model_view(Role, 'bulk_rename', path='rename', detail=False) +class RoleBulkRenameView(generic.BulkRenameView): + queryset = Role.objects.all() + + @register_model_view(Role, 'bulk_delete', path='delete', detail=False) class RoleBulkDeleteView(generic.BulkDeleteView): queryset = Role.objects.all() @@ -506,6 +538,7 @@ class PrefixListView(generic.ObjectListView): filterset_form = forms.PrefixFilterForm table = tables.PrefixTable template_name = 'ipam/prefix_list.html' + actions = (AddObject, BulkImport, BulkExport, BulkEdit, BulkDelete) @register_model_view(Prefix) @@ -766,6 +799,11 @@ class IPRangeBulkEditView(generic.BulkEditView): form = forms.IPRangeBulkEditForm +@register_model_view(IPRange, 'bulk_rename', path='rename', detail=False) +class IPRangeBulkRenameView(generic.BulkRenameView): + queryset = IPRange.objects.all() + + @register_model_view(IPRange, 'bulk_delete', path='delete', detail=False) class IPRangeBulkDeleteView(generic.BulkDeleteView): queryset = IPRange.objects.all() @@ -783,6 +821,7 @@ class IPAddressListView(generic.ObjectListView): filterset = filtersets.IPAddressFilterSet filterset_form = forms.IPAddressFilterForm table = tables.IPAddressTable + actions = (AddObject, BulkImport, BulkExport, BulkEdit, BulkDelete) @register_model_view(IPAddress) @@ -1006,6 +1045,11 @@ class VLANGroupBulkEditView(generic.BulkEditView): form = forms.VLANGroupBulkEditForm +@register_model_view(VLANGroup, 'bulk_rename', path='rename', detail=False) +class VLANGroupBulkRenameView(generic.BulkRenameView): + queryset = VLANGroup.objects.all() + + @register_model_view(VLANGroup, 'bulk_delete', path='delete', detail=False) class VLANGroupBulkDeleteView(generic.BulkDeleteView): queryset = VLANGroup.objects.annotate_utilization().prefetch_related('tags') @@ -1095,6 +1139,11 @@ class VLANTranslationPolicyBulkEditView(generic.BulkEditView): form = forms.VLANTranslationPolicyBulkEditForm +@register_model_view(VLANTranslationPolicy, 'bulk_rename', path='rename', detail=False) +class VLANTranslationPolicyBulkRenameView(generic.BulkRenameView): + queryset = VLANTranslationPolicy.objects.all() + + @register_model_view(VLANTranslationPolicy, 'bulk_delete', path='delete', detail=False) class VLANTranslationPolicyBulkDeleteView(generic.BulkDeleteView): queryset = VLANTranslationPolicy.objects.all() @@ -1112,6 +1161,7 @@ class VLANTranslationRuleListView(generic.ObjectListView): filterset = filtersets.VLANTranslationRuleFilterSet filterset_form = forms.VLANTranslationRuleFilterForm table = tables.VLANTranslationRuleTable + actions = (AddObject, BulkImport, BulkExport, BulkEdit, BulkDelete) @register_model_view(VLANTranslationRule) @@ -1244,6 +1294,11 @@ class FHRPGroupBulkEditView(generic.BulkEditView): form = forms.FHRPGroupBulkEditForm +@register_model_view(FHRPGroup, 'bulk_rename', path='rename', detail=False) +class FHRPGroupBulkRenameView(generic.BulkRenameView): + queryset = FHRPGroup.objects.all() + + @register_model_view(FHRPGroup, 'bulk_delete', path='delete', detail=False) class FHRPGroupBulkDeleteView(generic.BulkDeleteView): queryset = FHRPGroup.objects.all() @@ -1371,6 +1426,11 @@ class VLANBulkEditView(generic.BulkEditView): form = forms.VLANBulkEditForm +@register_model_view(VLAN, 'bulk_rename', path='rename', detail=False) +class VLANBulkRenameView(generic.BulkRenameView): + queryset = VLAN.objects.all() + + @register_model_view(VLAN, 'bulk_delete', path='delete', detail=False) class VLANBulkDeleteView(generic.BulkDeleteView): queryset = VLAN.objects.all() @@ -1421,6 +1481,11 @@ class ServiceTemplateBulkEditView(generic.BulkEditView): form = forms.ServiceTemplateBulkEditForm +@register_model_view(ServiceTemplate, 'bulk_rename', path='rename', detail=False) +class ServiceTemplateBulkRenameView(generic.BulkRenameView): + queryset = ServiceTemplate.objects.all() + + @register_model_view(ServiceTemplate, 'bulk_delete', path='delete', detail=False) class ServiceTemplateBulkDeleteView(generic.BulkDeleteView): queryset = ServiceTemplate.objects.all() @@ -1488,6 +1553,11 @@ class ServiceBulkEditView(generic.BulkEditView): form = forms.ServiceBulkEditForm +@register_model_view(Service, 'bulk_rename', path='rename', detail=False) +class ServiceBulkRenameView(generic.BulkRenameView): + queryset = Service.objects.all() + + @register_model_view(Service, 'bulk_delete', path='delete', detail=False) class ServiceBulkDeleteView(generic.BulkDeleteView): queryset = Service.objects.prefetch_related('parent') diff --git a/netbox/netbox/object_actions.py b/netbox/netbox/object_actions.py index b427d58cd..73315ce4c 100644 --- a/netbox/netbox/object_actions.py +++ b/netbox/netbox/object_actions.py @@ -1,4 +1,5 @@ from django.urls import reverse +from django.urls.exceptions import NoReverseMatch from django.utils.translation import gettext as _ from core.models import ObjectType @@ -42,7 +43,10 @@ class ObjectAction: kwargs = { kwarg: getattr(obj, kwarg) for kwarg in cls.url_kwargs } - return reverse(viewname, kwargs=kwargs) + try: + return reverse(viewname, kwargs=kwargs) + except NoReverseMatch: + return @classmethod def get_context(cls, context, obj): diff --git a/netbox/netbox/views/generic/bulk_views.py b/netbox/netbox/views/generic/bulk_views.py index 08b060634..9872a158c 100644 --- a/netbox/netbox/views/generic/bulk_views.py +++ b/netbox/netbox/views/generic/bulk_views.py @@ -22,7 +22,7 @@ from core.models import ObjectType from core.signals import clear_events from extras.choices import CustomFieldUIEditableChoices from extras.models import CustomField, ExportTemplate -from netbox.object_actions import AddObject, BulkDelete, BulkEdit, BulkExport, BulkImport +from netbox.object_actions import AddObject, BulkDelete, BulkEdit, BulkExport, BulkImport, BulkRename from utilities.error_handlers import handle_protectederror from utilities.exceptions import AbortRequest, AbortTransaction, PermissionsViolation from utilities.forms import BulkRenameForm, ConfirmationForm, restrict_form_fields @@ -55,13 +55,12 @@ class ObjectListView(BaseMultiObjectView, ActionsMixin, TableMixin): Attributes: filterset: A django-filter FilterSet that is applied to the queryset filterset_form: The form class used to render filter options - actions: A mapping of supported actions to their required permissions. When adding custom actions, bulk - action names must be prefixed with `bulk_`. (See ActionsMixin.) + actions: An iterable of ObjectAction subclasses (see ActionsMixin) """ template_name = 'generic/object_list.html' filterset = None filterset_form = None - actions = (AddObject, BulkImport, BulkEdit, BulkExport, BulkDelete) + actions = (AddObject, BulkImport, BulkExport, BulkEdit, BulkRename, BulkDelete) def get_required_permission(self): return get_permission_for_model(self.queryset.model, 'view') @@ -731,7 +730,11 @@ class BulkEditView(GetReturnURLMixin, BaseMultiObjectView): class BulkRenameView(GetReturnURLMixin, BaseMultiObjectView): """ An extendable view for renaming objects in bulk. + + Attributes: + field_name: The name of the object attribute for which the value is being updated (defaults to "name") """ + field_name = 'name' template_name = 'generic/bulk_rename.html' def __init__(self, *args, **kwargs): @@ -761,12 +764,12 @@ class BulkRenameView(GetReturnURLMixin, BaseMultiObjectView): replace = form.cleaned_data['replace'] if form.cleaned_data['use_regex']: try: - obj.new_name = re.sub(find, replace, obj.name or '') + obj.new_name = re.sub(find, replace, getattr(obj, self.field_name, '')) # Catch regex group reference errors except re.error: - obj.new_name = obj.name + obj.new_name = getattr(obj, self.field_name) else: - obj.new_name = (obj.name or '').replace(find, replace) + obj.new_name = getattr(obj, self.field_name, '').replace(find, replace) renamed_pks.append(obj.pk) return renamed_pks @@ -785,7 +788,7 @@ class BulkRenameView(GetReturnURLMixin, BaseMultiObjectView): if '_apply' in request.POST: for obj in selected_objects: - obj.name = obj.new_name + setattr(obj, self.field_name, obj.new_name) obj.save() # Enforce constrained permissions @@ -815,6 +818,7 @@ class BulkRenameView(GetReturnURLMixin, BaseMultiObjectView): selected_objects = self.queryset.filter(pk__in=form.initial['pk']) return render(request, self.template_name, { + 'field_name': self.field_name, 'form': form, 'obj_type_plural': self.queryset.model._meta.verbose_name_plural, 'selected_objects': selected_objects, diff --git a/netbox/netbox/views/generic/object_views.py b/netbox/netbox/views/generic/object_views.py index 2e9b73d20..5bc79d962 100644 --- a/netbox/netbox/views/generic/object_views.py +++ b/netbox/netbox/views/generic/object_views.py @@ -47,6 +47,7 @@ class ObjectView(ActionsMixin, BaseObjectView): Attributes: tab: A ViewTab instance for the view + actions: An iterable of ObjectAction subclasses (see ActionsMixin) """ tab = None actions = (CloneObject, EditObject, DeleteObject) @@ -96,8 +97,7 @@ class ObjectChildrenView(ObjectView, ActionsMixin, TableMixin): table: The django-tables2 Table class used to render the child objects list filterset: A django-filter FilterSet that is applied to the queryset filterset_form: The form class used to render filter options - actions: A mapping of supported actions to their required permissions. When adding custom actions, bulk - action names must be prefixed with `bulk_`. (See ActionsMixin.) + actions: An iterable of ObjectAction subclasses (see ActionsMixin) """ child_model = None table = None diff --git a/netbox/templates/generic/bulk_rename.html b/netbox/templates/generic/bulk_rename.html index ce9d95c65..5c18a5e44 100644 --- a/netbox/templates/generic/bulk_rename.html +++ b/netbox/templates/generic/bulk_rename.html @@ -42,10 +42,12 @@ Context: {% for obj in selected_objects %} - - {{ obj.name }} - {{ obj.new_name }} - + {% with obj_name=obj|getattr:field_name %} + + {{ obj_name }} + {{ obj.new_name }} + + {% endwith %} {% endfor %} diff --git a/netbox/templates/htmx/table.html b/netbox/templates/htmx/table.html index 49788ffa9..cb80125cf 100644 --- a/netbox/templates/htmx/table.html +++ b/netbox/templates/htmx/table.html @@ -27,12 +27,7 @@ {# Update the bulk action buttons with new query parameters #} {% if actions %}
- {% if 'bulk_edit' in actions %} - {% bulk_edit_button model query_params=request.GET %} - {% endif %} - {% if 'bulk_delete' in actions %} - {% bulk_delete_button model query_params=request.GET %} - {% endif %} + {% action_buttons actions model multi=True %}
{% endif %} {% endif %} diff --git a/netbox/tenancy/views.py b/netbox/tenancy/views.py index 83794f21f..246f0a200 100644 --- a/netbox/tenancy/views.py +++ b/netbox/tenancy/views.py @@ -71,6 +71,11 @@ class TenantGroupBulkEditView(generic.BulkEditView): form = forms.TenantGroupBulkEditForm +@register_model_view(TenantGroup, 'bulk_rename', path='rename', detail=False) +class TenantGroupBulkRenameView(generic.BulkRenameView): + queryset = TenantGroup.objects.all() + + @register_model_view(TenantGroup, 'bulk_delete', path='delete', detail=False) class TenantGroupBulkDeleteView(generic.BulkDeleteView): queryset = TenantGroup.objects.add_related_count( @@ -132,6 +137,11 @@ class TenantBulkEditView(generic.BulkEditView): form = forms.TenantBulkEditForm +@register_model_view(Tenant, 'bulk_rename', path='rename', detail=False) +class TenantBulkRenameView(generic.BulkRenameView): + queryset = Tenant.objects.all() + + @register_model_view(Tenant, 'bulk_delete', path='delete', detail=False) class TenantBulkDeleteView(generic.BulkDeleteView): queryset = Tenant.objects.all() @@ -207,6 +217,11 @@ class ContactGroupBulkEditView(generic.BulkEditView): form = forms.ContactGroupBulkEditForm +@register_model_view(ContactGroup, 'bulk_rename', path='rename', detail=False) +class ContactGroupBulkRenameView(generic.BulkRenameView): + queryset = ContactGroup.objects.all() + + @register_model_view(ContactGroup, 'bulk_delete', path='delete', detail=False) class ContactGroupBulkDeleteView(generic.BulkDeleteView): queryset = ContactGroup.objects.add_related_count( @@ -268,6 +283,11 @@ class ContactRoleBulkEditView(generic.BulkEditView): form = forms.ContactRoleBulkEditForm +@register_model_view(ContactRole, 'bulk_rename', path='rename', detail=False) +class ContactRoleBulkRenameView(generic.BulkRenameView): + queryset = ContactRole.objects.all() + + @register_model_view(ContactRole, 'bulk_delete', path='delete', detail=False) class ContactRoleBulkDeleteView(generic.BulkDeleteView): queryset = ContactRole.objects.all() @@ -331,6 +351,11 @@ class ContactBulkEditView(generic.BulkEditView): obj.groups.remove(*form.cleaned_data['remove_groups']) +@register_model_view(Contact, 'bulk_rename', path='rename', detail=False) +class ContactBulkRenameView(generic.BulkRenameView): + queryset = Contact.objects.all() + + @register_model_view(Contact, 'bulk_delete', path='delete', detail=False) class ContactBulkDeleteView(generic.BulkDeleteView): queryset = Contact.objects.annotate( diff --git a/netbox/users/views.py b/netbox/users/views.py index 099bbcf87..9071c6c8b 100644 --- a/netbox/users/views.py +++ b/netbox/users/views.py @@ -2,6 +2,7 @@ from django.db.models import Count from core.models import ObjectChange from core.tables import ObjectChangeTable +from netbox.object_actions import AddObject, BulkDelete, BulkEdit, BulkExport, BulkImport, BulkRename from netbox.views import generic from utilities.views import register_model_view from . import filtersets, forms, tables @@ -18,6 +19,7 @@ class TokenListView(generic.ObjectListView): filterset = filtersets.TokenFilterSet filterset_form = forms.TokenFilterForm table = tables.TokenTable + actions = (AddObject, BulkImport, BulkExport, BulkEdit, BulkDelete) @register_model_view(Token) @@ -111,6 +113,12 @@ class UserBulkEditView(generic.BulkEditView): form = forms.UserBulkEditForm +@register_model_view(User, 'bulk_rename', path='rename', detail=False) +class UserBulkRenameView(generic.BulkRenameView): + queryset = User.objects.all() + field_name = 'username' + + @register_model_view(User, 'bulk_delete', path='delete', detail=False) class UserBulkDeleteView(generic.BulkDeleteView): queryset = User.objects.all() @@ -162,6 +170,11 @@ class GroupBulkEditView(generic.BulkEditView): form = forms.GroupBulkEditForm +@register_model_view(Group, 'bulk_rename', path='rename', detail=False) +class GroupBulkRenameView(generic.BulkRenameView): + queryset = Group.objects.all() + + @register_model_view(Group, 'bulk_delete', path='delete', detail=False) class GroupBulkDeleteView(generic.BulkDeleteView): queryset = Group.objects.annotate(users_count=Count('user')).order_by('name') @@ -179,6 +192,7 @@ class ObjectPermissionListView(generic.ObjectListView): filterset = filtersets.ObjectPermissionFilterSet filterset_form = forms.ObjectPermissionFilterForm table = tables.ObjectPermissionTable + actions = (AddObject, BulkExport, BulkEdit, BulkRename, BulkDelete) @register_model_view(ObjectPermission) @@ -207,6 +221,11 @@ class ObjectPermissionBulkEditView(generic.BulkEditView): form = forms.ObjectPermissionBulkEditForm +@register_model_view(ObjectPermission, 'bulk_rename', path='rename', detail=False) +class ObjectPermissionBulkRenameView(generic.BulkRenameView): + queryset = ObjectPermission.objects.all() + + @register_model_view(ObjectPermission, 'bulk_delete', path='delete', detail=False) class ObjectPermissionBulkDeleteView(generic.BulkDeleteView): queryset = ObjectPermission.objects.all() diff --git a/netbox/utilities/templates/buttons/bulk_rename.html b/netbox/utilities/templates/buttons/bulk_rename.html index 376faa88b..6e268cc62 100644 --- a/netbox/utilities/templates/buttons/bulk_rename.html +++ b/netbox/utilities/templates/buttons/bulk_rename.html @@ -1,3 +1,5 @@ - +{% if url %} + +{% endif %} diff --git a/netbox/utilities/templatetags/builtins/filters.py b/netbox/utilities/templatetags/builtins/filters.py index b1c8c524b..2d4cbba69 100644 --- a/netbox/utilities/templatetags/builtins/filters.py +++ b/netbox/utilities/templatetags/builtins/filters.py @@ -22,6 +22,7 @@ __all__ = ( 'content_type', 'content_type_id', 'fgcolor', + 'getattr_', 'isodate', 'isodatetime', 'isotime', @@ -88,6 +89,14 @@ def fgcolor(value, dark='000000', light='ffffff'): return f'#{foreground_color(value, dark, light)}' +@register.filter('getattr') +def getattr_(instance, name): + """ + Call getattr() on the object for the specified attribute. + """ + return getattr(instance, name, None) + + @register.filter() def meta(model, attr): """ diff --git a/netbox/virtualization/views.py b/netbox/virtualization/views.py index e6ab07571..358ab2414 100644 --- a/netbox/virtualization/views.py +++ b/netbox/virtualization/views.py @@ -13,7 +13,9 @@ from dcim.tables import DeviceTable from extras.views import ObjectConfigContextView, ObjectRenderConfigView from ipam.models import IPAddress, VLANGroup from ipam.tables import InterfaceVLANTable, VLANTranslationRuleTable -from netbox.object_actions import * +from netbox.object_actions import ( + AddObject, BulkDelete, BulkEdit, BulkExport, BulkImport, BulkRename, DeleteObject, EditObject, +) from netbox.views import generic from utilities.query import count_related from utilities.query_functions import CollateAsChar @@ -75,6 +77,11 @@ class ClusterTypeBulkEditView(generic.BulkEditView): form = forms.ClusterTypeBulkEditForm +@register_model_view(ClusterType, 'bulk_rename', path='rename', detail=False) +class ClusterTypeBulkRenameView(generic.BulkRenameView): + queryset = ClusterType.objects.all() + + @register_model_view(ClusterType, 'bulk_delete', path='delete', detail=False) class ClusterTypeBulkDeleteView(generic.BulkDeleteView): queryset = ClusterType.objects.annotate( @@ -148,6 +155,11 @@ class ClusterGroupBulkEditView(generic.BulkEditView): form = forms.ClusterGroupBulkEditForm +@register_model_view(ClusterGroup, 'bulk_rename', path='rename', detail=False) +class ClusterGroupBulkRenameView(generic.BulkRenameView): + queryset = ClusterGroup.objects.all() + + @register_model_view(ClusterGroup, 'bulk_delete', path='delete', detail=False) class ClusterGroupBulkDeleteView(generic.BulkDeleteView): queryset = ClusterGroup.objects.annotate( @@ -262,6 +274,11 @@ class ClusterBulkEditView(generic.BulkEditView): form = forms.ClusterBulkEditForm +@register_model_view(Cluster, 'bulk_rename', path='rename', detail=False) +class ClusterBulkRenameView(generic.BulkRenameView): + queryset = Cluster.objects.all() + + @register_model_view(Cluster, 'bulk_delete', path='delete', detail=False) class ClusterBulkDeleteView(generic.BulkDeleteView): queryset = Cluster.objects.all() @@ -322,7 +339,7 @@ class VirtualMachineListView(generic.ObjectListView): filterset = filtersets.VirtualMachineFilterSet filterset_form = forms.VirtualMachineFilterForm table = tables.VirtualMachineTable - actions = (AddObject, BulkImport, BulkExport, BulkAddComponents, BulkEdit, BulkDelete) + actions = (AddObject, BulkImport, BulkExport, BulkAddComponents, BulkEdit, BulkRename, BulkDelete) @register_model_view(VirtualMachine) @@ -417,6 +434,11 @@ class VirtualMachineBulkEditView(generic.BulkEditView): form = forms.VirtualMachineBulkEditForm +@register_model_view(VirtualMachine, 'bulk_rename', path='rename', detail=False) +class VirtualMachineBulkRenameView(generic.BulkRenameView): + queryset = VirtualMachine.objects.all() + + @register_model_view(VirtualMachine, 'bulk_delete', path='delete', detail=False) class VirtualMachineBulkDeleteView(generic.BulkDeleteView): queryset = VirtualMachine.objects.prefetch_related('primary_ip4', 'primary_ip6') diff --git a/netbox/vpn/views.py b/netbox/vpn/views.py index fa7302251..af9bf1b34 100644 --- a/netbox/vpn/views.py +++ b/netbox/vpn/views.py @@ -1,4 +1,5 @@ from ipam.tables import RouteTargetTable +from netbox.object_actions import AddObject, BulkDelete, BulkEdit, BulkExport, BulkImport from netbox.views import generic from utilities.query import count_related from utilities.views import GetRelatedModelsMixin, register_model_view @@ -58,6 +59,11 @@ class TunnelGroupBulkEditView(generic.BulkEditView): form = forms.TunnelGroupBulkEditForm +@register_model_view(TunnelGroup, 'bulk_rename', path='rename', detail=False) +class TunnelGroupBulkRenameView(generic.BulkRenameView): + queryset = TunnelGroup.objects.all() + + @register_model_view(TunnelGroup, 'bulk_delete', path='delete', detail=False) class TunnelGroupBulkDeleteView(generic.BulkDeleteView): queryset = TunnelGroup.objects.annotate( @@ -122,6 +128,11 @@ class TunnelBulkEditView(generic.BulkEditView): form = forms.TunnelBulkEditForm +@register_model_view(Tunnel, 'bulk_rename', path='rename', detail=False) +class TunnelBulkRenameView(generic.BulkRenameView): + queryset = Tunnel.objects.all() + + @register_model_view(Tunnel, 'bulk_delete', path='delete', detail=False) class TunnelBulkDeleteView(generic.BulkDeleteView): queryset = Tunnel.objects.annotate( @@ -224,6 +235,11 @@ class IKEProposalBulkEditView(generic.BulkEditView): form = forms.IKEProposalBulkEditForm +@register_model_view(IKEProposal, 'bulk_rename', path='rename', detail=False) +class IKEProposalBulkRenameView(generic.BulkRenameView): + queryset = IKEProposal.objects.all() + + @register_model_view(IKEProposal, 'bulk_delete', path='delete', detail=False) class IKEProposalBulkDeleteView(generic.BulkDeleteView): queryset = IKEProposal.objects.all() @@ -274,6 +290,11 @@ class IKEPolicyBulkEditView(generic.BulkEditView): form = forms.IKEPolicyBulkEditForm +@register_model_view(IKEPolicy, 'bulk_rename', path='rename', detail=False) +class IKEPolicyBulkRenameView(generic.BulkRenameView): + queryset = IKEPolicy.objects.all() + + @register_model_view(IKEPolicy, 'bulk_delete', path='delete', detail=False) class IKEPolicyBulkDeleteView(generic.BulkDeleteView): queryset = IKEPolicy.objects.all() @@ -324,6 +345,11 @@ class IPSecProposalBulkEditView(generic.BulkEditView): form = forms.IPSecProposalBulkEditForm +@register_model_view(IPSecProposal, 'bulk_rename', path='rename', detail=False) +class IPSecProposalBulkRenameView(generic.BulkRenameView): + queryset = IPSecProposal.objects.all() + + @register_model_view(IPSecProposal, 'bulk_delete', path='delete', detail=False) class IPSecProposalBulkDeleteView(generic.BulkDeleteView): queryset = IPSecProposal.objects.all() @@ -374,6 +400,11 @@ class IPSecPolicyBulkEditView(generic.BulkEditView): form = forms.IPSecPolicyBulkEditForm +@register_model_view(IPSecPolicy, 'bulk_rename', path='rename', detail=False) +class IPSecPolicyBulkRenameView(generic.BulkRenameView): + queryset = IPSecPolicy.objects.all() + + @register_model_view(IPSecPolicy, 'bulk_delete', path='delete', detail=False) class IPSecPolicyBulkDeleteView(generic.BulkDeleteView): queryset = IPSecPolicy.objects.all() @@ -424,6 +455,11 @@ class IPSecProfileBulkEditView(generic.BulkEditView): form = forms.IPSecProfileBulkEditForm +@register_model_view(IPSecProfile, 'bulk_rename', path='rename', detail=False) +class IPSecProfileBulkRenameView(generic.BulkRenameView): + queryset = IPSecProfile.objects.all() + + @register_model_view(IPSecProfile, 'bulk_delete', path='delete', detail=False) class IPSecProfileBulkDeleteView(generic.BulkDeleteView): queryset = IPSecProfile.objects.all() @@ -491,6 +527,11 @@ class L2VPNBulkEditView(generic.BulkEditView): form = forms.L2VPNBulkEditForm +@register_model_view(L2VPN, 'bulk_rename', path='rename', detail=False) +class L2VPNBulkRenameView(generic.BulkRenameView): + queryset = L2VPN.objects.all() + + @register_model_view(L2VPN, 'bulk_delete', path='delete', detail=False) class L2VPNBulkDeleteView(generic.BulkDeleteView): queryset = L2VPN.objects.all() @@ -508,6 +549,7 @@ class L2VPNTerminationListView(generic.ObjectListView): table = tables.L2VPNTerminationTable filterset = filtersets.L2VPNTerminationFilterSet filterset_form = forms.L2VPNTerminationFilterForm + actions = (AddObject, BulkImport, BulkExport, BulkEdit, BulkDelete) @register_model_view(L2VPNTermination) diff --git a/netbox/wireless/views.py b/netbox/wireless/views.py index 405560fd9..cc8f9d995 100644 --- a/netbox/wireless/views.py +++ b/netbox/wireless/views.py @@ -68,6 +68,11 @@ class WirelessLANGroupBulkEditView(generic.BulkEditView): form = forms.WirelessLANGroupBulkEditForm +@register_model_view(WirelessLANGroup, 'bulk_rename', path='rename', detail=False) +class WirelessLANGroupBulkRenameView(generic.BulkRenameView): + queryset = WirelessLANGroup.objects.all() + + @register_model_view(WirelessLANGroup, 'bulk_delete', path='delete', detail=False) class WirelessLANGroupBulkDeleteView(generic.BulkDeleteView): queryset = WirelessLANGroup.objects.add_related_count( @@ -137,6 +142,12 @@ class WirelessLANBulkEditView(generic.BulkEditView): form = forms.WirelessLANBulkEditForm +@register_model_view(WirelessLAN, 'bulk_rename', path='rename', detail=False) +class WirelessLANBulkRenameView(generic.BulkRenameView): + queryset = WirelessLAN.objects.all() + field_name = 'ssid' + + @register_model_view(WirelessLAN, 'bulk_delete', path='delete', detail=False) class WirelessLANBulkDeleteView(generic.BulkDeleteView): queryset = WirelessLAN.objects.all() @@ -187,6 +198,12 @@ class WirelessLinkBulkEditView(generic.BulkEditView): form = forms.WirelessLinkBulkEditForm +@register_model_view(WirelessLink, 'bulk_rename', path='rename', detail=False) +class WirelessLinkBulkRenameView(generic.BulkRenameView): + queryset = WirelessLink.objects.all() + field_name = 'ssid' + + @register_model_view(WirelessLink, 'bulk_delete', path='delete', detail=False) class WirelessLinkBulkDeleteView(generic.BulkDeleteView): queryset = WirelessLink.objects.all()