Skip to content

Commit a4b6777

Browse files
committed
Enable bulk editing of nested group models
1 parent 3251a73 commit a4b6777

File tree

8 files changed

+181
-0
lines changed

8 files changed

+181
-0
lines changed

netbox/dcim/forms.py

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -201,6 +201,24 @@ class Meta:
201201
fields = Region.csv_headers
202202

203203

204+
class RegionBulkEditForm(BootstrapMixin, CustomFieldBulkEditForm):
205+
pk = forms.ModelMultipleChoiceField(
206+
queryset=Region.objects.all(),
207+
widget=forms.MultipleHiddenInput
208+
)
209+
parent = DynamicModelChoiceField(
210+
queryset=Region.objects.all(),
211+
required=False
212+
)
213+
description = forms.CharField(
214+
max_length=200,
215+
required=False
216+
)
217+
218+
class Meta:
219+
nullable_fields = ['parent', 'description']
220+
221+
204222
class RegionFilterForm(BootstrapMixin, forms.Form):
205223
model = Site
206224
q = forms.CharField(
@@ -240,6 +258,24 @@ class Meta:
240258
fields = SiteGroup.csv_headers
241259

242260

261+
class SiteGroupBulkEditForm(BootstrapMixin, CustomFieldBulkEditForm):
262+
pk = forms.ModelMultipleChoiceField(
263+
queryset=SiteGroup.objects.all(),
264+
widget=forms.MultipleHiddenInput
265+
)
266+
parent = DynamicModelChoiceField(
267+
queryset=SiteGroup.objects.all(),
268+
required=False
269+
)
270+
description = forms.CharField(
271+
max_length=200,
272+
required=False
273+
)
274+
275+
class Meta:
276+
nullable_fields = ['parent', 'description']
277+
278+
243279
class SiteGroupFilterForm(BootstrapMixin, forms.Form):
244280
model = Site
245281
q = forms.CharField(
@@ -480,6 +516,31 @@ class Meta:
480516
fields = Location.csv_headers
481517

482518

519+
class LocationBulkEditForm(BootstrapMixin, CustomFieldBulkEditForm):
520+
pk = forms.ModelMultipleChoiceField(
521+
queryset=Location.objects.all(),
522+
widget=forms.MultipleHiddenInput
523+
)
524+
site = DynamicModelChoiceField(
525+
queryset=Site.objects.all(),
526+
required=False
527+
)
528+
parent = DynamicModelChoiceField(
529+
queryset=Location.objects.all(),
530+
required=False,
531+
query_params={
532+
'site_id': '$site'
533+
}
534+
)
535+
description = forms.CharField(
536+
max_length=200,
537+
required=False
538+
)
539+
540+
class Meta:
541+
nullable_fields = ['parent', 'description']
542+
543+
483544
class LocationFilterForm(BootstrapMixin, forms.Form):
484545
region_id = DynamicModelMultipleChoiceField(
485546
queryset=Region.objects.all(),

netbox/dcim/tests/test_views.py

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,44 @@ def setUpTestData(cls):
5757
"Region 6,region-6,Sixth region",
5858
)
5959

60+
cls.bulk_edit_data = {
61+
'description': 'New description',
62+
}
63+
64+
65+
class SiteGroupTestCase(ViewTestCases.OrganizationalObjectViewTestCase):
66+
model = SiteGroup
67+
68+
@classmethod
69+
def setUpTestData(cls):
70+
71+
# Create three SiteGroups
72+
sitegroups = (
73+
SiteGroup(name='Site Group 1', slug='site-group-1'),
74+
SiteGroup(name='Site Group 2', slug='site-group-2'),
75+
SiteGroup(name='Site Group 3', slug='site-group-3'),
76+
)
77+
for sitegroup in sitegroups:
78+
sitegroup.save()
79+
80+
cls.form_data = {
81+
'name': 'Site Group X',
82+
'slug': 'site-group-x',
83+
'parent': sitegroups[2].pk,
84+
'description': 'A new site group',
85+
}
86+
87+
cls.csv_data = (
88+
"name,slug,description",
89+
"Site Group 4,site-group-4,Fourth site group",
90+
"Site Group 5,site-group-5,Fifth site group",
91+
"Site Group 6,site-group-6,Sixth site group",
92+
)
93+
94+
cls.bulk_edit_data = {
95+
'description': 'New description',
96+
}
97+
6098

6199
class SiteTestCase(ViewTestCases.PrimaryObjectViewTestCase):
62100
model = Site
@@ -157,6 +195,10 @@ def setUpTestData(cls):
157195
"Site 1,Location 6,location-6,Sixth location",
158196
)
159197

198+
cls.bulk_edit_data = {
199+
'description': 'New description',
200+
}
201+
160202

161203
class RackRoleTestCase(ViewTestCases.OrganizationalObjectViewTestCase):
162204
model = RackRole

netbox/dcim/urls.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
path('regions/', views.RegionListView.as_view(), name='region_list'),
1313
path('regions/add/', views.RegionEditView.as_view(), name='region_add'),
1414
path('regions/import/', views.RegionBulkImportView.as_view(), name='region_import'),
15+
path('regions/edit/', views.RegionBulkEditView.as_view(), name='region_bulk_edit'),
1516
path('regions/delete/', views.RegionBulkDeleteView.as_view(), name='region_bulk_delete'),
1617
path('regions/<int:pk>/edit/', views.RegionEditView.as_view(), name='region_edit'),
1718
path('regions/<int:pk>/delete/', views.RegionDeleteView.as_view(), name='region_delete'),
@@ -21,6 +22,7 @@
2122
path('site-groups/', views.SiteGroupListView.as_view(), name='sitegroup_list'),
2223
path('site-groups/add/', views.SiteGroupEditView.as_view(), name='sitegroup_add'),
2324
path('site-groups/import/', views.SiteGroupBulkImportView.as_view(), name='sitegroup_import'),
25+
path('site-groups/edit/', views.SiteGroupBulkEditView.as_view(), name='sitegroup_bulk_edit'),
2426
path('site-groups/delete/', views.SiteGroupBulkDeleteView.as_view(), name='sitegroup_bulk_delete'),
2527
path('site-groups/<int:pk>/edit/', views.SiteGroupEditView.as_view(), name='sitegroup_edit'),
2628
path('site-groups/<int:pk>/delete/', views.SiteGroupDeleteView.as_view(), name='sitegroup_delete'),
@@ -42,6 +44,7 @@
4244
path('locations/', views.LocationListView.as_view(), name='location_list'),
4345
path('locations/add/', views.LocationEditView.as_view(), name='location_add'),
4446
path('locations/import/', views.LocationBulkImportView.as_view(), name='location_import'),
47+
path('locations/edit/', views.LocationBulkEditView.as_view(), name='location_bulk_edit'),
4548
path('locations/delete/', views.LocationBulkDeleteView.as_view(), name='location_bulk_delete'),
4649
path('locations/<int:pk>/edit/', views.LocationEditView.as_view(), name='location_edit'),
4750
path('locations/<int:pk>/delete/', views.LocationDeleteView.as_view(), name='location_delete'),

netbox/dcim/views.py

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -126,6 +126,19 @@ class RegionBulkImportView(generic.BulkImportView):
126126
table = tables.RegionTable
127127

128128

129+
class RegionBulkEditView(generic.BulkEditView):
130+
queryset = Region.objects.add_related_count(
131+
Region.objects.all(),
132+
Site,
133+
'region',
134+
'site_count',
135+
cumulative=True
136+
)
137+
filterset = filters.RegionFilterSet
138+
table = tables.RegionTable
139+
form = forms.RegionBulkEditForm
140+
141+
129142
class RegionBulkDeleteView(generic.BulkDeleteView):
130143
queryset = Region.objects.add_related_count(
131144
Region.objects.all(),
@@ -170,6 +183,19 @@ class SiteGroupBulkImportView(generic.BulkImportView):
170183
table = tables.SiteGroupTable
171184

172185

186+
class SiteGroupBulkEditView(generic.BulkEditView):
187+
queryset = SiteGroup.objects.add_related_count(
188+
SiteGroup.objects.all(),
189+
Site,
190+
'group',
191+
'site_count',
192+
cumulative=True
193+
)
194+
filterset = filters.SiteGroupFilterSet
195+
table = tables.SiteGroupTable
196+
form = forms.SiteGroupBulkEditForm
197+
198+
173199
class SiteGroupBulkDeleteView(generic.BulkDeleteView):
174200
queryset = SiteGroup.objects.add_related_count(
175201
SiteGroup.objects.all(),
@@ -279,6 +305,19 @@ class LocationBulkImportView(generic.BulkImportView):
279305
table = tables.LocationTable
280306

281307

308+
class LocationBulkEditView(generic.BulkEditView):
309+
queryset = Location.objects.add_related_count(
310+
Location.objects.all(),
311+
Rack,
312+
'location',
313+
'rack_count',
314+
cumulative=True
315+
).prefetch_related('site')
316+
filterset = filters.LocationFilterSet
317+
table = tables.LocationTable
318+
form = forms.LocationBulkEditForm
319+
320+
282321
class LocationBulkDeleteView(generic.BulkDeleteView):
283322
queryset = Location.objects.add_related_count(
284323
Location.objects.all(),

netbox/tenancy/forms.py

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,24 @@ class Meta:
4444
fields = TenantGroup.csv_headers
4545

4646

47+
class TenantGroupBulkEditForm(BootstrapMixin, CustomFieldBulkEditForm):
48+
pk = forms.ModelMultipleChoiceField(
49+
queryset=TenantGroup.objects.all(),
50+
widget=forms.MultipleHiddenInput
51+
)
52+
parent = DynamicModelChoiceField(
53+
queryset=TenantGroup.objects.all(),
54+
required=False
55+
)
56+
description = forms.CharField(
57+
max_length=200,
58+
required=False
59+
)
60+
61+
class Meta:
62+
nullable_fields = ['parent', 'description']
63+
64+
4765
#
4866
# Tenants
4967
#

netbox/tenancy/tests/test_views.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,10 @@ def setUpTestData(cls):
2929
"Tenant Group 6,tenant-group-6,Sixth tenant group",
3030
)
3131

32+
cls.bulk_edit_data = {
33+
'description': 'New description',
34+
}
35+
3236

3337
class TenantTestCase(ViewTestCases.PrimaryObjectViewTestCase):
3438
model = Tenant

netbox/tenancy/urls.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
path('tenant-groups/', views.TenantGroupListView.as_view(), name='tenantgroup_list'),
1212
path('tenant-groups/add/', views.TenantGroupEditView.as_view(), name='tenantgroup_add'),
1313
path('tenant-groups/import/', views.TenantGroupBulkImportView.as_view(), name='tenantgroup_import'),
14+
path('tenant-groups/edit/', views.TenantGroupBulkEditView.as_view(), name='tenantgroup_bulk_edit'),
1415
path('tenant-groups/delete/', views.TenantGroupBulkDeleteView.as_view(), name='tenantgroup_bulk_delete'),
1516
path('tenant-groups/<int:pk>/edit/', views.TenantGroupEditView.as_view(), name='tenantgroup_edit'),
1617
path('tenant-groups/<int:pk>/delete/', views.TenantGroupDeleteView.as_view(), name='tenantgroup_delete'),

netbox/tenancy/views.py

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,19 @@ class TenantGroupBulkImportView(generic.BulkImportView):
3939
table = tables.TenantGroupTable
4040

4141

42+
class TenantGroupBulkEditView(generic.BulkEditView):
43+
queryset = TenantGroup.objects.add_related_count(
44+
TenantGroup.objects.all(),
45+
Tenant,
46+
'group',
47+
'tenant_count',
48+
cumulative=True
49+
)
50+
filterset = filters.TenantGroupFilterSet
51+
table = tables.TenantGroupTable
52+
form = forms.TenantGroupBulkEditForm
53+
54+
4255
class TenantGroupBulkDeleteView(generic.BulkDeleteView):
4356
queryset = TenantGroup.objects.add_related_count(
4457
TenantGroup.objects.all(),

0 commit comments

Comments
 (0)