mirror of
https://github.com/netbox-community/netbox.git
synced 2025-07-27 02:48:38 -06:00
Enable bulk editing of nested group models
This commit is contained in:
parent
3251a73d86
commit
a4b6777e4e
@ -201,6 +201,24 @@ class RegionCSVForm(CustomFieldModelCSVForm):
|
|||||||
fields = Region.csv_headers
|
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):
|
class RegionFilterForm(BootstrapMixin, forms.Form):
|
||||||
model = Site
|
model = Site
|
||||||
q = forms.CharField(
|
q = forms.CharField(
|
||||||
@ -240,6 +258,24 @@ class SiteGroupCSVForm(CustomFieldModelCSVForm):
|
|||||||
fields = SiteGroup.csv_headers
|
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):
|
class SiteGroupFilterForm(BootstrapMixin, forms.Form):
|
||||||
model = Site
|
model = Site
|
||||||
q = forms.CharField(
|
q = forms.CharField(
|
||||||
@ -480,6 +516,31 @@ class LocationCSVForm(CustomFieldModelCSVForm):
|
|||||||
fields = Location.csv_headers
|
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):
|
class LocationFilterForm(BootstrapMixin, forms.Form):
|
||||||
region_id = DynamicModelMultipleChoiceField(
|
region_id = DynamicModelMultipleChoiceField(
|
||||||
queryset=Region.objects.all(),
|
queryset=Region.objects.all(),
|
||||||
|
@ -57,6 +57,44 @@ class RegionTestCase(ViewTestCases.OrganizationalObjectViewTestCase):
|
|||||||
"Region 6,region-6,Sixth region",
|
"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):
|
class SiteTestCase(ViewTestCases.PrimaryObjectViewTestCase):
|
||||||
model = Site
|
model = Site
|
||||||
@ -157,6 +195,10 @@ class LocationTestCase(ViewTestCases.OrganizationalObjectViewTestCase):
|
|||||||
"Site 1,Location 6,location-6,Sixth location",
|
"Site 1,Location 6,location-6,Sixth location",
|
||||||
)
|
)
|
||||||
|
|
||||||
|
cls.bulk_edit_data = {
|
||||||
|
'description': 'New description',
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
class RackRoleTestCase(ViewTestCases.OrganizationalObjectViewTestCase):
|
class RackRoleTestCase(ViewTestCases.OrganizationalObjectViewTestCase):
|
||||||
model = RackRole
|
model = RackRole
|
||||||
|
@ -12,6 +12,7 @@ urlpatterns = [
|
|||||||
path('regions/', views.RegionListView.as_view(), name='region_list'),
|
path('regions/', views.RegionListView.as_view(), name='region_list'),
|
||||||
path('regions/add/', views.RegionEditView.as_view(), name='region_add'),
|
path('regions/add/', views.RegionEditView.as_view(), name='region_add'),
|
||||||
path('regions/import/', views.RegionBulkImportView.as_view(), name='region_import'),
|
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/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>/edit/', views.RegionEditView.as_view(), name='region_edit'),
|
||||||
path('regions/<int:pk>/delete/', views.RegionDeleteView.as_view(), name='region_delete'),
|
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/', views.SiteGroupListView.as_view(), name='sitegroup_list'),
|
||||||
path('site-groups/add/', views.SiteGroupEditView.as_view(), name='sitegroup_add'),
|
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/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/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>/edit/', views.SiteGroupEditView.as_view(), name='sitegroup_edit'),
|
||||||
path('site-groups/<int:pk>/delete/', views.SiteGroupDeleteView.as_view(), name='sitegroup_delete'),
|
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/', views.LocationListView.as_view(), name='location_list'),
|
||||||
path('locations/add/', views.LocationEditView.as_view(), name='location_add'),
|
path('locations/add/', views.LocationEditView.as_view(), name='location_add'),
|
||||||
path('locations/import/', views.LocationBulkImportView.as_view(), name='location_import'),
|
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/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>/edit/', views.LocationEditView.as_view(), name='location_edit'),
|
||||||
path('locations/<int:pk>/delete/', views.LocationDeleteView.as_view(), name='location_delete'),
|
path('locations/<int:pk>/delete/', views.LocationDeleteView.as_view(), name='location_delete'),
|
||||||
|
@ -126,6 +126,19 @@ class RegionBulkImportView(generic.BulkImportView):
|
|||||||
table = tables.RegionTable
|
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):
|
class RegionBulkDeleteView(generic.BulkDeleteView):
|
||||||
queryset = Region.objects.add_related_count(
|
queryset = Region.objects.add_related_count(
|
||||||
Region.objects.all(),
|
Region.objects.all(),
|
||||||
@ -170,6 +183,19 @@ class SiteGroupBulkImportView(generic.BulkImportView):
|
|||||||
table = tables.SiteGroupTable
|
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):
|
class SiteGroupBulkDeleteView(generic.BulkDeleteView):
|
||||||
queryset = SiteGroup.objects.add_related_count(
|
queryset = SiteGroup.objects.add_related_count(
|
||||||
SiteGroup.objects.all(),
|
SiteGroup.objects.all(),
|
||||||
@ -279,6 +305,19 @@ class LocationBulkImportView(generic.BulkImportView):
|
|||||||
table = tables.LocationTable
|
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):
|
class LocationBulkDeleteView(generic.BulkDeleteView):
|
||||||
queryset = Location.objects.add_related_count(
|
queryset = Location.objects.add_related_count(
|
||||||
Location.objects.all(),
|
Location.objects.all(),
|
||||||
|
@ -44,6 +44,24 @@ class TenantGroupCSVForm(CustomFieldModelCSVForm):
|
|||||||
fields = TenantGroup.csv_headers
|
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
|
# Tenants
|
||||||
#
|
#
|
||||||
|
@ -29,6 +29,10 @@ class TenantGroupTestCase(ViewTestCases.OrganizationalObjectViewTestCase):
|
|||||||
"Tenant Group 6,tenant-group-6,Sixth tenant group",
|
"Tenant Group 6,tenant-group-6,Sixth tenant group",
|
||||||
)
|
)
|
||||||
|
|
||||||
|
cls.bulk_edit_data = {
|
||||||
|
'description': 'New description',
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
class TenantTestCase(ViewTestCases.PrimaryObjectViewTestCase):
|
class TenantTestCase(ViewTestCases.PrimaryObjectViewTestCase):
|
||||||
model = Tenant
|
model = Tenant
|
||||||
|
@ -11,6 +11,7 @@ urlpatterns = [
|
|||||||
path('tenant-groups/', views.TenantGroupListView.as_view(), name='tenantgroup_list'),
|
path('tenant-groups/', views.TenantGroupListView.as_view(), name='tenantgroup_list'),
|
||||||
path('tenant-groups/add/', views.TenantGroupEditView.as_view(), name='tenantgroup_add'),
|
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/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/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>/edit/', views.TenantGroupEditView.as_view(), name='tenantgroup_edit'),
|
||||||
path('tenant-groups/<int:pk>/delete/', views.TenantGroupDeleteView.as_view(), name='tenantgroup_delete'),
|
path('tenant-groups/<int:pk>/delete/', views.TenantGroupDeleteView.as_view(), name='tenantgroup_delete'),
|
||||||
|
@ -39,6 +39,19 @@ class TenantGroupBulkImportView(generic.BulkImportView):
|
|||||||
table = tables.TenantGroupTable
|
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):
|
class TenantGroupBulkDeleteView(generic.BulkDeleteView):
|
||||||
queryset = TenantGroup.objects.add_related_count(
|
queryset = TenantGroup.objects.add_related_count(
|
||||||
TenantGroup.objects.all(),
|
TenantGroup.objects.all(),
|
||||||
|
Loading…
Reference in New Issue
Block a user