mirror of
https://github.com/netbox-community/netbox.git
synced 2024-05-10 07:54:54 +00:00
* Enable bulk editing of organizational models * Enable bulk editing of nested group models * Changelog for #5972
This commit is contained in:
@ -71,6 +71,7 @@ The ObjectChange model (which is used to record the creation, modification, and
|
||||
* [#5894](https://github.com/netbox-community/netbox/issues/5894) - Use primary keys when filtering object lists by related objects in the UI
|
||||
* [#5895](https://github.com/netbox-community/netbox/issues/5895) - Rename RackGroup to Location
|
||||
* [#5901](https://github.com/netbox-community/netbox/issues/5901) - Add `created` and `last_updated` fields to device component models
|
||||
* [#5972](https://github.com/netbox-community/netbox/issues/5972) - Enable bulk editing for organizational models
|
||||
|
||||
### Other Changes
|
||||
|
||||
|
@ -142,6 +142,20 @@ class CircuitTypeForm(BootstrapMixin, CustomFieldModelForm):
|
||||
]
|
||||
|
||||
|
||||
class CircuitTypeBulkEditForm(BootstrapMixin, CustomFieldBulkEditForm):
|
||||
pk = forms.ModelMultipleChoiceField(
|
||||
queryset=CircuitType.objects.all(),
|
||||
widget=forms.MultipleHiddenInput
|
||||
)
|
||||
description = forms.CharField(
|
||||
max_length=200,
|
||||
required=False
|
||||
)
|
||||
|
||||
class Meta:
|
||||
nullable_fields = ['description']
|
||||
|
||||
|
||||
class CircuitTypeCSVForm(CustomFieldModelCSVForm):
|
||||
slug = SlugField()
|
||||
|
||||
|
@ -73,6 +73,10 @@ class CircuitTypeTestCase(ViewTestCases.OrganizationalObjectViewTestCase):
|
||||
"Circuit Type 6,circuit-type-6",
|
||||
)
|
||||
|
||||
cls.bulk_edit_data = {
|
||||
'description': 'Foo',
|
||||
}
|
||||
|
||||
|
||||
class CircuitTestCase(ViewTestCases.PrimaryObjectViewTestCase):
|
||||
model = Circuit
|
||||
|
@ -23,6 +23,7 @@ urlpatterns = [
|
||||
path('circuit-types/', views.CircuitTypeListView.as_view(), name='circuittype_list'),
|
||||
path('circuit-types/add/', views.CircuitTypeEditView.as_view(), name='circuittype_add'),
|
||||
path('circuit-types/import/', views.CircuitTypeBulkImportView.as_view(), name='circuittype_import'),
|
||||
path('circuit-types/edit/', views.CircuitTypeBulkEditView.as_view(), name='circuittype_bulk_edit'),
|
||||
path('circuit-types/delete/', views.CircuitTypeBulkDeleteView.as_view(), name='circuittype_bulk_delete'),
|
||||
path('circuit-types/<int:pk>/edit/', views.CircuitTypeEditView.as_view(), name='circuittype_edit'),
|
||||
path('circuit-types/<int:pk>/delete/', views.CircuitTypeDeleteView.as_view(), name='circuittype_delete'),
|
||||
|
@ -107,6 +107,15 @@ class CircuitTypeBulkImportView(generic.BulkImportView):
|
||||
table = tables.CircuitTypeTable
|
||||
|
||||
|
||||
class CircuitTypeBulkEditView(generic.BulkEditView):
|
||||
queryset = CircuitType.objects.annotate(
|
||||
circuit_count=count_related(Circuit, 'type')
|
||||
)
|
||||
filterset = filters.CircuitTypeFilterSet
|
||||
table = tables.CircuitTypeTable
|
||||
form = forms.CircuitTypeBulkEditForm
|
||||
|
||||
|
||||
class CircuitTypeBulkDeleteView(generic.BulkDeleteView):
|
||||
queryset = CircuitType.objects.annotate(
|
||||
circuit_count=count_related(Circuit, 'type')
|
||||
|
@ -201,6 +201,24 @@ class RegionCSVForm(CustomFieldModelCSVForm):
|
||||
fields = Region.csv_headers
|
||||
|
||||
|
||||
class RegionBulkEditForm(BootstrapMixin, CustomFieldBulkEditForm):
|
||||
pk = forms.ModelMultipleChoiceField(
|
||||
queryset=Region.objects.all(),
|
||||
widget=forms.MultipleHiddenInput
|
||||
)
|
||||
parent = DynamicModelChoiceField(
|
||||
queryset=Region.objects.all(),
|
||||
required=False
|
||||
)
|
||||
description = forms.CharField(
|
||||
max_length=200,
|
||||
required=False
|
||||
)
|
||||
|
||||
class Meta:
|
||||
nullable_fields = ['parent', 'description']
|
||||
|
||||
|
||||
class RegionFilterForm(BootstrapMixin, forms.Form):
|
||||
model = Site
|
||||
q = forms.CharField(
|
||||
@ -240,6 +258,24 @@ class SiteGroupCSVForm(CustomFieldModelCSVForm):
|
||||
fields = SiteGroup.csv_headers
|
||||
|
||||
|
||||
class SiteGroupBulkEditForm(BootstrapMixin, CustomFieldBulkEditForm):
|
||||
pk = forms.ModelMultipleChoiceField(
|
||||
queryset=SiteGroup.objects.all(),
|
||||
widget=forms.MultipleHiddenInput
|
||||
)
|
||||
parent = DynamicModelChoiceField(
|
||||
queryset=SiteGroup.objects.all(),
|
||||
required=False
|
||||
)
|
||||
description = forms.CharField(
|
||||
max_length=200,
|
||||
required=False
|
||||
)
|
||||
|
||||
class Meta:
|
||||
nullable_fields = ['parent', 'description']
|
||||
|
||||
|
||||
class SiteGroupFilterForm(BootstrapMixin, forms.Form):
|
||||
model = Site
|
||||
q = forms.CharField(
|
||||
@ -480,6 +516,31 @@ class LocationCSVForm(CustomFieldModelCSVForm):
|
||||
fields = Location.csv_headers
|
||||
|
||||
|
||||
class LocationBulkEditForm(BootstrapMixin, CustomFieldBulkEditForm):
|
||||
pk = forms.ModelMultipleChoiceField(
|
||||
queryset=Location.objects.all(),
|
||||
widget=forms.MultipleHiddenInput
|
||||
)
|
||||
site = DynamicModelChoiceField(
|
||||
queryset=Site.objects.all(),
|
||||
required=False
|
||||
)
|
||||
parent = DynamicModelChoiceField(
|
||||
queryset=Location.objects.all(),
|
||||
required=False,
|
||||
query_params={
|
||||
'site_id': '$site'
|
||||
}
|
||||
)
|
||||
description = forms.CharField(
|
||||
max_length=200,
|
||||
required=False
|
||||
)
|
||||
|
||||
class Meta:
|
||||
nullable_fields = ['parent', 'description']
|
||||
|
||||
|
||||
class LocationFilterForm(BootstrapMixin, forms.Form):
|
||||
region_id = DynamicModelMultipleChoiceField(
|
||||
queryset=Region.objects.all(),
|
||||
@ -530,6 +591,25 @@ class RackRoleCSVForm(CustomFieldModelCSVForm):
|
||||
}
|
||||
|
||||
|
||||
class RackRoleBulkEditForm(BootstrapMixin, CustomFieldBulkEditForm):
|
||||
pk = forms.ModelMultipleChoiceField(
|
||||
queryset=RackRole.objects.all(),
|
||||
widget=forms.MultipleHiddenInput
|
||||
)
|
||||
color = forms.CharField(
|
||||
max_length=6, # RGB color code
|
||||
required=False,
|
||||
widget=ColorSelect()
|
||||
)
|
||||
description = forms.CharField(
|
||||
max_length=200,
|
||||
required=False
|
||||
)
|
||||
|
||||
class Meta:
|
||||
nullable_fields = ['color', 'description']
|
||||
|
||||
|
||||
#
|
||||
# Racks
|
||||
#
|
||||
@ -1026,6 +1106,20 @@ class ManufacturerCSVForm(CustomFieldModelCSVForm):
|
||||
fields = Manufacturer.csv_headers
|
||||
|
||||
|
||||
class ManufacturerBulkEditForm(BootstrapMixin, CustomFieldBulkEditForm):
|
||||
pk = forms.ModelMultipleChoiceField(
|
||||
queryset=Manufacturer.objects.all(),
|
||||
widget=forms.MultipleHiddenInput
|
||||
)
|
||||
description = forms.CharField(
|
||||
max_length=200,
|
||||
required=False
|
||||
)
|
||||
|
||||
class Meta:
|
||||
nullable_fields = ['description']
|
||||
|
||||
|
||||
#
|
||||
# Device types
|
||||
#
|
||||
@ -1822,6 +1916,30 @@ class DeviceRoleCSVForm(CustomFieldModelCSVForm):
|
||||
}
|
||||
|
||||
|
||||
class DeviceRoleBulkEditForm(BootstrapMixin, CustomFieldBulkEditForm):
|
||||
pk = forms.ModelMultipleChoiceField(
|
||||
queryset=DeviceRole.objects.all(),
|
||||
widget=forms.MultipleHiddenInput
|
||||
)
|
||||
color = forms.CharField(
|
||||
max_length=6, # RGB color code
|
||||
required=False,
|
||||
widget=ColorSelect()
|
||||
)
|
||||
vm_role = forms.NullBooleanField(
|
||||
required=False,
|
||||
widget=BulkEditNullBooleanSelect,
|
||||
label='VM role'
|
||||
)
|
||||
description = forms.CharField(
|
||||
max_length=200,
|
||||
required=False
|
||||
)
|
||||
|
||||
class Meta:
|
||||
nullable_fields = ['color', 'description']
|
||||
|
||||
|
||||
#
|
||||
# Platforms
|
||||
#
|
||||
@ -1859,6 +1977,29 @@ class PlatformCSVForm(CustomFieldModelCSVForm):
|
||||
fields = Platform.csv_headers
|
||||
|
||||
|
||||
class PlatformBulkEditForm(BootstrapMixin, CustomFieldBulkEditForm):
|
||||
pk = forms.ModelMultipleChoiceField(
|
||||
queryset=Platform.objects.all(),
|
||||
widget=forms.MultipleHiddenInput
|
||||
)
|
||||
manufacturer = DynamicModelChoiceField(
|
||||
queryset=Manufacturer.objects.all(),
|
||||
required=False
|
||||
)
|
||||
napalm_driver = forms.CharField(
|
||||
max_length=50,
|
||||
required=False
|
||||
)
|
||||
# TODO: Bulk edit support for napalm_args
|
||||
description = forms.CharField(
|
||||
max_length=200,
|
||||
required=False
|
||||
)
|
||||
|
||||
class Meta:
|
||||
nullable_fields = ['manufacturer', 'napalm_driver', 'description']
|
||||
|
||||
|
||||
#
|
||||
# Devices
|
||||
#
|
||||
|
@ -57,6 +57,44 @@ class RegionTestCase(ViewTestCases.OrganizationalObjectViewTestCase):
|
||||
"Region 6,region-6,Sixth region",
|
||||
)
|
||||
|
||||
cls.bulk_edit_data = {
|
||||
'description': 'New description',
|
||||
}
|
||||
|
||||
|
||||
class SiteGroupTestCase(ViewTestCases.OrganizationalObjectViewTestCase):
|
||||
model = SiteGroup
|
||||
|
||||
@classmethod
|
||||
def setUpTestData(cls):
|
||||
|
||||
# Create three SiteGroups
|
||||
sitegroups = (
|
||||
SiteGroup(name='Site Group 1', slug='site-group-1'),
|
||||
SiteGroup(name='Site Group 2', slug='site-group-2'),
|
||||
SiteGroup(name='Site Group 3', slug='site-group-3'),
|
||||
)
|
||||
for sitegroup in sitegroups:
|
||||
sitegroup.save()
|
||||
|
||||
cls.form_data = {
|
||||
'name': 'Site Group X',
|
||||
'slug': 'site-group-x',
|
||||
'parent': sitegroups[2].pk,
|
||||
'description': 'A new site group',
|
||||
}
|
||||
|
||||
cls.csv_data = (
|
||||
"name,slug,description",
|
||||
"Site Group 4,site-group-4,Fourth site group",
|
||||
"Site Group 5,site-group-5,Fifth site group",
|
||||
"Site Group 6,site-group-6,Sixth site group",
|
||||
)
|
||||
|
||||
cls.bulk_edit_data = {
|
||||
'description': 'New description',
|
||||
}
|
||||
|
||||
|
||||
class SiteTestCase(ViewTestCases.PrimaryObjectViewTestCase):
|
||||
model = Site
|
||||
@ -157,6 +195,10 @@ class LocationTestCase(ViewTestCases.OrganizationalObjectViewTestCase):
|
||||
"Site 1,Location 6,location-6,Sixth location",
|
||||
)
|
||||
|
||||
cls.bulk_edit_data = {
|
||||
'description': 'New description',
|
||||
}
|
||||
|
||||
|
||||
class RackRoleTestCase(ViewTestCases.OrganizationalObjectViewTestCase):
|
||||
model = RackRole
|
||||
@ -184,6 +226,11 @@ class RackRoleTestCase(ViewTestCases.OrganizationalObjectViewTestCase):
|
||||
"Rack Role 6,rack-role-6,0000ff",
|
||||
)
|
||||
|
||||
cls.bulk_edit_data = {
|
||||
'color': '00ff00',
|
||||
'description': 'New description',
|
||||
}
|
||||
|
||||
|
||||
class RackReservationTestCase(ViewTestCases.PrimaryObjectViewTestCase):
|
||||
model = RackReservation
|
||||
@ -345,6 +392,10 @@ class ManufacturerTestCase(ViewTestCases.OrganizationalObjectViewTestCase):
|
||||
"Manufacturer 6,manufacturer-6,Sixth manufacturer",
|
||||
)
|
||||
|
||||
cls.bulk_edit_data = {
|
||||
'description': 'New description',
|
||||
}
|
||||
|
||||
|
||||
# TODO: Change base class to PrimaryObjectViewTestCase
|
||||
# Blocked by absence of bulk import view for DeviceTypes
|
||||
@ -894,6 +945,11 @@ class DeviceRoleTestCase(ViewTestCases.OrganizationalObjectViewTestCase):
|
||||
"Device Role 6,device-role-6,0000ff",
|
||||
)
|
||||
|
||||
cls.bulk_edit_data = {
|
||||
'color': '00ff00',
|
||||
'description': 'New description',
|
||||
}
|
||||
|
||||
|
||||
class PlatformTestCase(ViewTestCases.OrganizationalObjectViewTestCase):
|
||||
model = Platform
|
||||
@ -925,6 +981,11 @@ class PlatformTestCase(ViewTestCases.OrganizationalObjectViewTestCase):
|
||||
"Platform 6,platform-6,Sixth platform",
|
||||
)
|
||||
|
||||
cls.bulk_edit_data = {
|
||||
'napalm_driver': 'ios',
|
||||
'description': 'New description',
|
||||
}
|
||||
|
||||
|
||||
class DeviceTestCase(ViewTestCases.PrimaryObjectViewTestCase):
|
||||
model = Device
|
||||
|
@ -12,6 +12,7 @@ urlpatterns = [
|
||||
path('regions/', views.RegionListView.as_view(), name='region_list'),
|
||||
path('regions/add/', views.RegionEditView.as_view(), name='region_add'),
|
||||
path('regions/import/', views.RegionBulkImportView.as_view(), name='region_import'),
|
||||
path('regions/edit/', views.RegionBulkEditView.as_view(), name='region_bulk_edit'),
|
||||
path('regions/delete/', views.RegionBulkDeleteView.as_view(), name='region_bulk_delete'),
|
||||
path('regions/<int:pk>/edit/', views.RegionEditView.as_view(), name='region_edit'),
|
||||
path('regions/<int:pk>/delete/', views.RegionDeleteView.as_view(), name='region_delete'),
|
||||
@ -21,6 +22,7 @@ urlpatterns = [
|
||||
path('site-groups/', views.SiteGroupListView.as_view(), name='sitegroup_list'),
|
||||
path('site-groups/add/', views.SiteGroupEditView.as_view(), name='sitegroup_add'),
|
||||
path('site-groups/import/', views.SiteGroupBulkImportView.as_view(), name='sitegroup_import'),
|
||||
path('site-groups/edit/', views.SiteGroupBulkEditView.as_view(), name='sitegroup_bulk_edit'),
|
||||
path('site-groups/delete/', views.SiteGroupBulkDeleteView.as_view(), name='sitegroup_bulk_delete'),
|
||||
path('site-groups/<int:pk>/edit/', views.SiteGroupEditView.as_view(), name='sitegroup_edit'),
|
||||
path('site-groups/<int:pk>/delete/', views.SiteGroupDeleteView.as_view(), name='sitegroup_delete'),
|
||||
@ -42,6 +44,7 @@ urlpatterns = [
|
||||
path('locations/', views.LocationListView.as_view(), name='location_list'),
|
||||
path('locations/add/', views.LocationEditView.as_view(), name='location_add'),
|
||||
path('locations/import/', views.LocationBulkImportView.as_view(), name='location_import'),
|
||||
path('locations/edit/', views.LocationBulkEditView.as_view(), name='location_bulk_edit'),
|
||||
path('locations/delete/', views.LocationBulkDeleteView.as_view(), name='location_bulk_delete'),
|
||||
path('locations/<int:pk>/edit/', views.LocationEditView.as_view(), name='location_edit'),
|
||||
path('locations/<int:pk>/delete/', views.LocationDeleteView.as_view(), name='location_delete'),
|
||||
@ -51,6 +54,7 @@ urlpatterns = [
|
||||
path('rack-roles/', views.RackRoleListView.as_view(), name='rackrole_list'),
|
||||
path('rack-roles/add/', views.RackRoleEditView.as_view(), name='rackrole_add'),
|
||||
path('rack-roles/import/', views.RackRoleBulkImportView.as_view(), name='rackrole_import'),
|
||||
path('rack-roles/edit/', views.RackRoleBulkEditView.as_view(), name='rackrole_bulk_edit'),
|
||||
path('rack-roles/delete/', views.RackRoleBulkDeleteView.as_view(), name='rackrole_bulk_delete'),
|
||||
path('rack-roles/<int:pk>/edit/', views.RackRoleEditView.as_view(), name='rackrole_edit'),
|
||||
path('rack-roles/<int:pk>/delete/', views.RackRoleDeleteView.as_view(), name='rackrole_delete'),
|
||||
@ -84,6 +88,7 @@ urlpatterns = [
|
||||
path('manufacturers/', views.ManufacturerListView.as_view(), name='manufacturer_list'),
|
||||
path('manufacturers/add/', views.ManufacturerEditView.as_view(), name='manufacturer_add'),
|
||||
path('manufacturers/import/', views.ManufacturerBulkImportView.as_view(), name='manufacturer_import'),
|
||||
path('manufacturers/edit/', views.ManufacturerBulkEditView.as_view(), name='manufacturer_bulk_edit'),
|
||||
path('manufacturers/delete/', views.ManufacturerBulkDeleteView.as_view(), name='manufacturer_bulk_delete'),
|
||||
path('manufacturers/<int:pk>/edit/', views.ManufacturerEditView.as_view(), name='manufacturer_edit'),
|
||||
path('manufacturers/<int:pk>/delete/', views.ManufacturerDeleteView.as_view(), name='manufacturer_delete'),
|
||||
@ -168,6 +173,7 @@ urlpatterns = [
|
||||
path('device-roles/', views.DeviceRoleListView.as_view(), name='devicerole_list'),
|
||||
path('device-roles/add/', views.DeviceRoleEditView.as_view(), name='devicerole_add'),
|
||||
path('device-roles/import/', views.DeviceRoleBulkImportView.as_view(), name='devicerole_import'),
|
||||
path('device-roles/edit/', views.DeviceRoleBulkEditView.as_view(), name='devicerole_bulk_edit'),
|
||||
path('device-roles/delete/', views.DeviceRoleBulkDeleteView.as_view(), name='devicerole_bulk_delete'),
|
||||
path('device-roles/<int:pk>/edit/', views.DeviceRoleEditView.as_view(), name='devicerole_edit'),
|
||||
path('device-roles/<int:pk>/delete/', views.DeviceRoleDeleteView.as_view(), name='devicerole_delete'),
|
||||
@ -177,6 +183,7 @@ urlpatterns = [
|
||||
path('platforms/', views.PlatformListView.as_view(), name='platform_list'),
|
||||
path('platforms/add/', views.PlatformEditView.as_view(), name='platform_add'),
|
||||
path('platforms/import/', views.PlatformBulkImportView.as_view(), name='platform_import'),
|
||||
path('platforms/edit/', views.PlatformBulkEditView.as_view(), name='platform_bulk_edit'),
|
||||
path('platforms/delete/', views.PlatformBulkDeleteView.as_view(), name='platform_bulk_delete'),
|
||||
path('platforms/<int:pk>/edit/', views.PlatformEditView.as_view(), name='platform_edit'),
|
||||
path('platforms/<int:pk>/delete/', views.PlatformDeleteView.as_view(), name='platform_delete'),
|
||||
|
@ -126,6 +126,19 @@ class RegionBulkImportView(generic.BulkImportView):
|
||||
table = tables.RegionTable
|
||||
|
||||
|
||||
class RegionBulkEditView(generic.BulkEditView):
|
||||
queryset = Region.objects.add_related_count(
|
||||
Region.objects.all(),
|
||||
Site,
|
||||
'region',
|
||||
'site_count',
|
||||
cumulative=True
|
||||
)
|
||||
filterset = filters.RegionFilterSet
|
||||
table = tables.RegionTable
|
||||
form = forms.RegionBulkEditForm
|
||||
|
||||
|
||||
class RegionBulkDeleteView(generic.BulkDeleteView):
|
||||
queryset = Region.objects.add_related_count(
|
||||
Region.objects.all(),
|
||||
@ -170,6 +183,19 @@ class SiteGroupBulkImportView(generic.BulkImportView):
|
||||
table = tables.SiteGroupTable
|
||||
|
||||
|
||||
class SiteGroupBulkEditView(generic.BulkEditView):
|
||||
queryset = SiteGroup.objects.add_related_count(
|
||||
SiteGroup.objects.all(),
|
||||
Site,
|
||||
'group',
|
||||
'site_count',
|
||||
cumulative=True
|
||||
)
|
||||
filterset = filters.SiteGroupFilterSet
|
||||
table = tables.SiteGroupTable
|
||||
form = forms.SiteGroupBulkEditForm
|
||||
|
||||
|
||||
class SiteGroupBulkDeleteView(generic.BulkDeleteView):
|
||||
queryset = SiteGroup.objects.add_related_count(
|
||||
SiteGroup.objects.all(),
|
||||
@ -279,6 +305,19 @@ class LocationBulkImportView(generic.BulkImportView):
|
||||
table = tables.LocationTable
|
||||
|
||||
|
||||
class LocationBulkEditView(generic.BulkEditView):
|
||||
queryset = Location.objects.add_related_count(
|
||||
Location.objects.all(),
|
||||
Rack,
|
||||
'location',
|
||||
'rack_count',
|
||||
cumulative=True
|
||||
).prefetch_related('site')
|
||||
filterset = filters.LocationFilterSet
|
||||
table = tables.LocationTable
|
||||
form = forms.LocationBulkEditForm
|
||||
|
||||
|
||||
class LocationBulkDeleteView(generic.BulkDeleteView):
|
||||
queryset = Location.objects.add_related_count(
|
||||
Location.objects.all(),
|
||||
@ -317,6 +356,15 @@ class RackRoleBulkImportView(generic.BulkImportView):
|
||||
table = tables.RackRoleTable
|
||||
|
||||
|
||||
class RackRoleBulkEditView(generic.BulkEditView):
|
||||
queryset = RackRole.objects.annotate(
|
||||
rack_count=count_related(Rack, 'role')
|
||||
)
|
||||
filterset = filters.RackRoleFilterSet
|
||||
table = tables.RackRoleTable
|
||||
form = forms.RackRoleBulkEditForm
|
||||
|
||||
|
||||
class RackRoleBulkDeleteView(generic.BulkDeleteView):
|
||||
queryset = RackRole.objects.annotate(
|
||||
rack_count=count_related(Rack, 'role')
|
||||
@ -534,6 +582,15 @@ class ManufacturerBulkImportView(generic.BulkImportView):
|
||||
table = tables.ManufacturerTable
|
||||
|
||||
|
||||
class ManufacturerBulkEditView(generic.BulkEditView):
|
||||
queryset = Manufacturer.objects.annotate(
|
||||
devicetype_count=count_related(DeviceType, 'manufacturer')
|
||||
)
|
||||
filterset = filters.ManufacturerFilterSet
|
||||
table = tables.ManufacturerTable
|
||||
form = forms.ManufacturerBulkEditForm
|
||||
|
||||
|
||||
class ManufacturerBulkDeleteView(generic.BulkDeleteView):
|
||||
queryset = Manufacturer.objects.annotate(
|
||||
devicetype_count=count_related(DeviceType, 'manufacturer')
|
||||
@ -975,6 +1032,13 @@ class DeviceRoleBulkImportView(generic.BulkImportView):
|
||||
table = tables.DeviceRoleTable
|
||||
|
||||
|
||||
class DeviceRoleBulkEditView(generic.BulkEditView):
|
||||
queryset = DeviceRole.objects.all()
|
||||
filterset = filters.DeviceRoleFilterSet
|
||||
table = tables.DeviceRoleTable
|
||||
form = forms.DeviceRoleBulkEditForm
|
||||
|
||||
|
||||
class DeviceRoleBulkDeleteView(generic.BulkDeleteView):
|
||||
queryset = DeviceRole.objects.all()
|
||||
table = tables.DeviceRoleTable
|
||||
@ -1007,6 +1071,13 @@ class PlatformBulkImportView(generic.BulkImportView):
|
||||
table = tables.PlatformTable
|
||||
|
||||
|
||||
class PlatformBulkEditView(generic.BulkEditView):
|
||||
queryset = Platform.objects.all()
|
||||
filterset = filters.PlatformFilterSet
|
||||
table = tables.PlatformTable
|
||||
form = forms.PlatformBulkEditForm
|
||||
|
||||
|
||||
class PlatformBulkDeleteView(generic.BulkDeleteView):
|
||||
queryset = Platform.objects.all()
|
||||
table = tables.PlatformTable
|
||||
|
@ -217,6 +217,24 @@ class RIRCSVForm(CustomFieldModelCSVForm):
|
||||
}
|
||||
|
||||
|
||||
class RIRBulkEditForm(BootstrapMixin, CustomFieldBulkEditForm):
|
||||
pk = forms.ModelMultipleChoiceField(
|
||||
queryset=RIR.objects.all(),
|
||||
widget=forms.MultipleHiddenInput
|
||||
)
|
||||
is_private = forms.NullBooleanField(
|
||||
required=False,
|
||||
widget=BulkEditNullBooleanSelect
|
||||
)
|
||||
description = forms.CharField(
|
||||
max_length=200,
|
||||
required=False
|
||||
)
|
||||
|
||||
class Meta:
|
||||
nullable_fields = ['is_private', 'description']
|
||||
|
||||
|
||||
class RIRFilterForm(BootstrapMixin, forms.Form):
|
||||
is_private = forms.NullBooleanField(
|
||||
required=False,
|
||||
@ -351,6 +369,23 @@ class RoleCSVForm(CustomFieldModelCSVForm):
|
||||
fields = Role.csv_headers
|
||||
|
||||
|
||||
class RoleBulkEditForm(BootstrapMixin, CustomFieldBulkEditForm):
|
||||
pk = forms.ModelMultipleChoiceField(
|
||||
queryset=Role.objects.all(),
|
||||
widget=forms.MultipleHiddenInput
|
||||
)
|
||||
weight = forms.IntegerField(
|
||||
required=False
|
||||
)
|
||||
description = forms.CharField(
|
||||
max_length=200,
|
||||
required=False
|
||||
)
|
||||
|
||||
class Meta:
|
||||
nullable_fields = ['description']
|
||||
|
||||
|
||||
#
|
||||
# Prefixes
|
||||
#
|
||||
@ -1154,6 +1189,24 @@ class VLANGroupCSVForm(CustomFieldModelCSVForm):
|
||||
fields = VLANGroup.csv_headers
|
||||
|
||||
|
||||
class VLANGroupBulkEditForm(BootstrapMixin, CustomFieldBulkEditForm):
|
||||
pk = forms.ModelMultipleChoiceField(
|
||||
queryset=VLANGroup.objects.all(),
|
||||
widget=forms.MultipleHiddenInput
|
||||
)
|
||||
site = DynamicModelChoiceField(
|
||||
queryset=Site.objects.all(),
|
||||
required=False
|
||||
)
|
||||
description = forms.CharField(
|
||||
max_length=200,
|
||||
required=False
|
||||
)
|
||||
|
||||
class Meta:
|
||||
nullable_fields = ['site', 'description']
|
||||
|
||||
|
||||
class VLANGroupFilterForm(BootstrapMixin, forms.Form):
|
||||
region_id = DynamicModelMultipleChoiceField(
|
||||
queryset=Region.objects.all(),
|
||||
|
@ -118,6 +118,10 @@ class RIRTestCase(ViewTestCases.OrganizationalObjectViewTestCase):
|
||||
"RIR 6,rir-6,Sixth RIR",
|
||||
)
|
||||
|
||||
cls.bulk_edit_data = {
|
||||
'description': 'New description',
|
||||
}
|
||||
|
||||
|
||||
class AggregateTestCase(ViewTestCases.PrimaryObjectViewTestCase):
|
||||
model = Aggregate
|
||||
@ -187,6 +191,10 @@ class RoleTestCase(ViewTestCases.OrganizationalObjectViewTestCase):
|
||||
"Role 6,role-6,1000",
|
||||
)
|
||||
|
||||
cls.bulk_edit_data = {
|
||||
'description': 'New description',
|
||||
}
|
||||
|
||||
|
||||
class PrefixTestCase(ViewTestCases.PrimaryObjectViewTestCase):
|
||||
model = Prefix
|
||||
@ -328,6 +336,10 @@ class VLANGroupTestCase(ViewTestCases.OrganizationalObjectViewTestCase):
|
||||
"VLAN Group 6,vlan-group-6,Sixth VLAN group",
|
||||
)
|
||||
|
||||
cls.bulk_edit_data = {
|
||||
'description': 'New description',
|
||||
}
|
||||
|
||||
|
||||
class VLANTestCase(ViewTestCases.PrimaryObjectViewTestCase):
|
||||
model = VLAN
|
||||
|
@ -33,6 +33,7 @@ urlpatterns = [
|
||||
path('rirs/', views.RIRListView.as_view(), name='rir_list'),
|
||||
path('rirs/add/', views.RIREditView.as_view(), name='rir_add'),
|
||||
path('rirs/import/', views.RIRBulkImportView.as_view(), name='rir_import'),
|
||||
path('rirs/edit/', views.RIRBulkEditView.as_view(), name='rir_bulk_edit'),
|
||||
path('rirs/delete/', views.RIRBulkDeleteView.as_view(), name='rir_bulk_delete'),
|
||||
path('rirs/<int:pk>/edit/', views.RIREditView.as_view(), name='rir_edit'),
|
||||
path('rirs/<int:pk>/delete/', views.RIRDeleteView.as_view(), name='rir_delete'),
|
||||
@ -53,6 +54,7 @@ urlpatterns = [
|
||||
path('roles/', views.RoleListView.as_view(), name='role_list'),
|
||||
path('roles/add/', views.RoleEditView.as_view(), name='role_add'),
|
||||
path('roles/import/', views.RoleBulkImportView.as_view(), name='role_import'),
|
||||
path('roles/edit/', views.RoleBulkEditView.as_view(), name='role_bulk_edit'),
|
||||
path('roles/delete/', views.RoleBulkDeleteView.as_view(), name='role_bulk_delete'),
|
||||
path('roles/<int:pk>/edit/', views.RoleEditView.as_view(), name='role_edit'),
|
||||
path('roles/<int:pk>/delete/', views.RoleDeleteView.as_view(), name='role_delete'),
|
||||
@ -88,6 +90,7 @@ urlpatterns = [
|
||||
path('vlan-groups/', views.VLANGroupListView.as_view(), name='vlangroup_list'),
|
||||
path('vlan-groups/add/', views.VLANGroupEditView.as_view(), name='vlangroup_add'),
|
||||
path('vlan-groups/import/', views.VLANGroupBulkImportView.as_view(), name='vlangroup_import'),
|
||||
path('vlan-groups/edit/', views.VLANGroupBulkEditView.as_view(), name='vlangroup_bulk_edit'),
|
||||
path('vlan-groups/delete/', views.VLANGroupBulkDeleteView.as_view(), name='vlangroup_bulk_delete'),
|
||||
path('vlan-groups/<int:pk>/edit/', views.VLANGroupEditView.as_view(), name='vlangroup_edit'),
|
||||
path('vlan-groups/<int:pk>/delete/', views.VLANGroupDeleteView.as_view(), name='vlangroup_delete'),
|
||||
|
@ -164,6 +164,15 @@ class RIRBulkImportView(generic.BulkImportView):
|
||||
table = tables.RIRTable
|
||||
|
||||
|
||||
class RIRBulkEditView(generic.BulkEditView):
|
||||
queryset = RIR.objects.annotate(
|
||||
aggregate_count=count_related(Aggregate, 'rir')
|
||||
)
|
||||
filterset = filters.RIRFilterSet
|
||||
table = tables.RIRTable
|
||||
form = forms.RIRBulkEditForm
|
||||
|
||||
|
||||
class RIRBulkDeleteView(generic.BulkDeleteView):
|
||||
queryset = RIR.objects.annotate(
|
||||
aggregate_count=count_related(Aggregate, 'rir')
|
||||
@ -298,6 +307,13 @@ class RoleBulkImportView(generic.BulkImportView):
|
||||
table = tables.RoleTable
|
||||
|
||||
|
||||
class RoleBulkEditView(generic.BulkEditView):
|
||||
queryset = Role.objects.all()
|
||||
filterset = filters.RoleFilterSet
|
||||
table = tables.RoleTable
|
||||
form = forms.RoleBulkEditForm
|
||||
|
||||
|
||||
class RoleBulkDeleteView(generic.BulkDeleteView):
|
||||
queryset = Role.objects.all()
|
||||
table = tables.RoleTable
|
||||
@ -654,6 +670,15 @@ class VLANGroupBulkImportView(generic.BulkImportView):
|
||||
table = tables.VLANGroupTable
|
||||
|
||||
|
||||
class VLANGroupBulkEditView(generic.BulkEditView):
|
||||
queryset = VLANGroup.objects.prefetch_related('site').annotate(
|
||||
vlan_count=count_related(VLAN, 'group')
|
||||
)
|
||||
filterset = filters.VLANGroupFilterSet
|
||||
table = tables.VLANGroupTable
|
||||
form = forms.VLANGroupBulkEditForm
|
||||
|
||||
|
||||
class VLANGroupBulkDeleteView(generic.BulkDeleteView):
|
||||
queryset = VLANGroup.objects.prefetch_related('site').annotate(
|
||||
vlan_count=count_related(VLAN, 'group')
|
||||
|
@ -59,6 +59,20 @@ class SecretRoleCSVForm(CustomFieldModelCSVForm):
|
||||
fields = SecretRole.csv_headers
|
||||
|
||||
|
||||
class SecretRoleBulkEditForm(BootstrapMixin, CustomFieldBulkEditForm):
|
||||
pk = forms.ModelMultipleChoiceField(
|
||||
queryset=SecretRole.objects.all(),
|
||||
widget=forms.MultipleHiddenInput
|
||||
)
|
||||
description = forms.CharField(
|
||||
max_length=200,
|
||||
required=False
|
||||
)
|
||||
|
||||
class Meta:
|
||||
nullable_fields = ['description']
|
||||
|
||||
|
||||
#
|
||||
# Secrets
|
||||
#
|
||||
|
@ -34,6 +34,10 @@ class SecretRoleTestCase(ViewTestCases.OrganizationalObjectViewTestCase):
|
||||
"Secret Role 6,secret-role-6",
|
||||
)
|
||||
|
||||
cls.bulk_edit_data = {
|
||||
'description': 'New description',
|
||||
}
|
||||
|
||||
|
||||
# TODO: Change base class to PrimaryObjectViewTestCase
|
||||
class SecretTestCase(
|
||||
|
@ -11,6 +11,7 @@ urlpatterns = [
|
||||
path('secret-roles/', views.SecretRoleListView.as_view(), name='secretrole_list'),
|
||||
path('secret-roles/add/', views.SecretRoleEditView.as_view(), name='secretrole_add'),
|
||||
path('secret-roles/import/', views.SecretRoleBulkImportView.as_view(), name='secretrole_import'),
|
||||
path('secret-roles/edit/', views.SecretRoleBulkEditView.as_view(), name='secretrole_bulk_edit'),
|
||||
path('secret-roles/delete/', views.SecretRoleBulkDeleteView.as_view(), name='secretrole_bulk_delete'),
|
||||
path('secret-roles/<int:pk>/edit/', views.SecretRoleEditView.as_view(), name='secretrole_edit'),
|
||||
path('secret-roles/<int:pk>/delete/', views.SecretRoleDeleteView.as_view(), name='secretrole_delete'),
|
||||
|
@ -48,6 +48,15 @@ class SecretRoleBulkImportView(generic.BulkImportView):
|
||||
table = tables.SecretRoleTable
|
||||
|
||||
|
||||
class SecretRoleBulkEditView(generic.BulkEditView):
|
||||
queryset = SecretRole.objects.annotate(
|
||||
secret_count=count_related(Secret, 'role')
|
||||
)
|
||||
filterset = filters.SecretRoleFilterSet
|
||||
table = tables.SecretRoleTable
|
||||
form = forms.SecretRoleBulkEditForm
|
||||
|
||||
|
||||
class SecretRoleBulkDeleteView(generic.BulkDeleteView):
|
||||
queryset = SecretRole.objects.annotate(
|
||||
secret_count=count_related(Secret, 'role')
|
||||
|
@ -44,6 +44,24 @@ class TenantGroupCSVForm(CustomFieldModelCSVForm):
|
||||
fields = TenantGroup.csv_headers
|
||||
|
||||
|
||||
class TenantGroupBulkEditForm(BootstrapMixin, CustomFieldBulkEditForm):
|
||||
pk = forms.ModelMultipleChoiceField(
|
||||
queryset=TenantGroup.objects.all(),
|
||||
widget=forms.MultipleHiddenInput
|
||||
)
|
||||
parent = DynamicModelChoiceField(
|
||||
queryset=TenantGroup.objects.all(),
|
||||
required=False
|
||||
)
|
||||
description = forms.CharField(
|
||||
max_length=200,
|
||||
required=False
|
||||
)
|
||||
|
||||
class Meta:
|
||||
nullable_fields = ['parent', 'description']
|
||||
|
||||
|
||||
#
|
||||
# Tenants
|
||||
#
|
||||
|
@ -29,6 +29,10 @@ class TenantGroupTestCase(ViewTestCases.OrganizationalObjectViewTestCase):
|
||||
"Tenant Group 6,tenant-group-6,Sixth tenant group",
|
||||
)
|
||||
|
||||
cls.bulk_edit_data = {
|
||||
'description': 'New description',
|
||||
}
|
||||
|
||||
|
||||
class TenantTestCase(ViewTestCases.PrimaryObjectViewTestCase):
|
||||
model = Tenant
|
||||
|
@ -11,6 +11,7 @@ urlpatterns = [
|
||||
path('tenant-groups/', views.TenantGroupListView.as_view(), name='tenantgroup_list'),
|
||||
path('tenant-groups/add/', views.TenantGroupEditView.as_view(), name='tenantgroup_add'),
|
||||
path('tenant-groups/import/', views.TenantGroupBulkImportView.as_view(), name='tenantgroup_import'),
|
||||
path('tenant-groups/edit/', views.TenantGroupBulkEditView.as_view(), name='tenantgroup_bulk_edit'),
|
||||
path('tenant-groups/delete/', views.TenantGroupBulkDeleteView.as_view(), name='tenantgroup_bulk_delete'),
|
||||
path('tenant-groups/<int:pk>/edit/', views.TenantGroupEditView.as_view(), name='tenantgroup_edit'),
|
||||
path('tenant-groups/<int:pk>/delete/', views.TenantGroupDeleteView.as_view(), name='tenantgroup_delete'),
|
||||
|
@ -39,6 +39,19 @@ class TenantGroupBulkImportView(generic.BulkImportView):
|
||||
table = tables.TenantGroupTable
|
||||
|
||||
|
||||
class TenantGroupBulkEditView(generic.BulkEditView):
|
||||
queryset = TenantGroup.objects.add_related_count(
|
||||
TenantGroup.objects.all(),
|
||||
Tenant,
|
||||
'group',
|
||||
'tenant_count',
|
||||
cumulative=True
|
||||
)
|
||||
filterset = filters.TenantGroupFilterSet
|
||||
table = tables.TenantGroupTable
|
||||
form = forms.TenantGroupBulkEditForm
|
||||
|
||||
|
||||
class TenantGroupBulkDeleteView(generic.BulkDeleteView):
|
||||
queryset = TenantGroup.objects.add_related_count(
|
||||
TenantGroup.objects.all(),
|
||||
|
@ -1024,6 +1024,7 @@ class ViewTestCases:
|
||||
DeleteObjectViewTestCase,
|
||||
ListObjectsViewTestCase,
|
||||
BulkImportObjectsViewTestCase,
|
||||
BulkEditObjectsViewTestCase,
|
||||
BulkDeleteObjectsViewTestCase,
|
||||
):
|
||||
"""
|
||||
|
@ -46,6 +46,20 @@ class ClusterTypeCSVForm(CustomFieldModelCSVForm):
|
||||
fields = ClusterType.csv_headers
|
||||
|
||||
|
||||
class ClusterTypeBulkEditForm(BootstrapMixin, CustomFieldBulkEditForm):
|
||||
pk = forms.ModelMultipleChoiceField(
|
||||
queryset=ClusterType.objects.all(),
|
||||
widget=forms.MultipleHiddenInput
|
||||
)
|
||||
description = forms.CharField(
|
||||
max_length=200,
|
||||
required=False
|
||||
)
|
||||
|
||||
class Meta:
|
||||
nullable_fields = ['description']
|
||||
|
||||
|
||||
#
|
||||
# Cluster groups
|
||||
#
|
||||
@ -68,6 +82,20 @@ class ClusterGroupCSVForm(CustomFieldModelCSVForm):
|
||||
fields = ClusterGroup.csv_headers
|
||||
|
||||
|
||||
class ClusterGroupBulkEditForm(BootstrapMixin, CustomFieldBulkEditForm):
|
||||
pk = forms.ModelMultipleChoiceField(
|
||||
queryset=ClusterGroup.objects.all(),
|
||||
widget=forms.MultipleHiddenInput
|
||||
)
|
||||
description = forms.CharField(
|
||||
max_length=200,
|
||||
required=False
|
||||
)
|
||||
|
||||
class Meta:
|
||||
nullable_fields = ['description']
|
||||
|
||||
|
||||
#
|
||||
# Clusters
|
||||
#
|
||||
|
@ -33,6 +33,10 @@ class ClusterGroupTestCase(ViewTestCases.OrganizationalObjectViewTestCase):
|
||||
"Cluster Group 6,cluster-group-6,Sixth cluster group",
|
||||
)
|
||||
|
||||
cls.bulk_edit_data = {
|
||||
'description': 'New description',
|
||||
}
|
||||
|
||||
|
||||
class ClusterTypeTestCase(ViewTestCases.OrganizationalObjectViewTestCase):
|
||||
model = ClusterType
|
||||
@ -59,6 +63,10 @@ class ClusterTypeTestCase(ViewTestCases.OrganizationalObjectViewTestCase):
|
||||
"Cluster Type 6,cluster-type-6,Sixth cluster type",
|
||||
)
|
||||
|
||||
cls.bulk_edit_data = {
|
||||
'description': 'New description',
|
||||
}
|
||||
|
||||
|
||||
class ClusterTestCase(ViewTestCases.PrimaryObjectViewTestCase):
|
||||
model = Cluster
|
||||
|
@ -12,6 +12,7 @@ urlpatterns = [
|
||||
path('cluster-types/', views.ClusterTypeListView.as_view(), name='clustertype_list'),
|
||||
path('cluster-types/add/', views.ClusterTypeEditView.as_view(), name='clustertype_add'),
|
||||
path('cluster-types/import/', views.ClusterTypeBulkImportView.as_view(), name='clustertype_import'),
|
||||
path('cluster-types/edit/', views.ClusterTypeBulkEditView.as_view(), name='clustertype_bulk_edit'),
|
||||
path('cluster-types/delete/', views.ClusterTypeBulkDeleteView.as_view(), name='clustertype_bulk_delete'),
|
||||
path('cluster-types/<int:pk>/edit/', views.ClusterTypeEditView.as_view(), name='clustertype_edit'),
|
||||
path('cluster-types/<int:pk>/delete/', views.ClusterTypeDeleteView.as_view(), name='clustertype_delete'),
|
||||
@ -21,6 +22,7 @@ urlpatterns = [
|
||||
path('cluster-groups/', views.ClusterGroupListView.as_view(), name='clustergroup_list'),
|
||||
path('cluster-groups/add/', views.ClusterGroupEditView.as_view(), name='clustergroup_add'),
|
||||
path('cluster-groups/import/', views.ClusterGroupBulkImportView.as_view(), name='clustergroup_import'),
|
||||
path('cluster-groups/edit/', views.ClusterGroupBulkEditView.as_view(), name='clustergroup_bulk_edit'),
|
||||
path('cluster-groups/delete/', views.ClusterGroupBulkDeleteView.as_view(), name='clustergroup_bulk_delete'),
|
||||
path('cluster-groups/<int:pk>/edit/', views.ClusterGroupEditView.as_view(), name='clustergroup_edit'),
|
||||
path('cluster-groups/<int:pk>/delete/', views.ClusterGroupDeleteView.as_view(), name='clustergroup_delete'),
|
||||
|
@ -42,6 +42,15 @@ class ClusterTypeBulkImportView(generic.BulkImportView):
|
||||
table = tables.ClusterTypeTable
|
||||
|
||||
|
||||
class ClusterTypeBulkEditView(generic.BulkEditView):
|
||||
queryset = ClusterType.objects.annotate(
|
||||
cluster_count=count_related(Cluster, 'type')
|
||||
)
|
||||
filterset = filters.ClusterTypeFilterSet
|
||||
table = tables.ClusterTypeTable
|
||||
form = forms.ClusterTypeBulkEditForm
|
||||
|
||||
|
||||
class ClusterTypeBulkDeleteView(generic.BulkDeleteView):
|
||||
queryset = ClusterType.objects.annotate(
|
||||
cluster_count=count_related(Cluster, 'type')
|
||||
@ -70,11 +79,22 @@ class ClusterGroupDeleteView(generic.ObjectDeleteView):
|
||||
|
||||
|
||||
class ClusterGroupBulkImportView(generic.BulkImportView):
|
||||
queryset = ClusterGroup.objects.all()
|
||||
queryset = ClusterGroup.objects.annotate(
|
||||
cluster_count=count_related(Cluster, 'group')
|
||||
)
|
||||
model_form = forms.ClusterGroupCSVForm
|
||||
table = tables.ClusterGroupTable
|
||||
|
||||
|
||||
class ClusterGroupBulkEditView(generic.BulkEditView):
|
||||
queryset = ClusterGroup.objects.annotate(
|
||||
cluster_count=count_related(Cluster, 'group')
|
||||
)
|
||||
filterset = filters.ClusterGroupFilterSet
|
||||
table = tables.ClusterGroupTable
|
||||
form = forms.ClusterGroupBulkEditForm
|
||||
|
||||
|
||||
class ClusterGroupBulkDeleteView(generic.BulkDeleteView):
|
||||
queryset = ClusterGroup.objects.annotate(
|
||||
cluster_count=count_related(Cluster, 'group')
|
||||
|
Reference in New Issue
Block a user