Skip to content

Commit 091d860

Browse files
Merge pull request #4114 from netbox-community/4113-component-bulk-editing
Closes #4113: Add bulk edit functionality for device type components
2 parents 077d692 + b5344b0 commit 091d860

File tree

8 files changed

+210
-17
lines changed

8 files changed

+210
-17
lines changed

netbox/dcim/forms.py

Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1058,6 +1058,21 @@ class ConsolePortTemplateCreateForm(BootstrapMixin, forms.Form):
10581058
)
10591059

10601060

1061+
class ConsolePortTemplateBulkEditForm(BootstrapMixin, BulkEditForm):
1062+
pk = forms.ModelMultipleChoiceField(
1063+
queryset=ConsolePortTemplate.objects.all(),
1064+
widget=forms.MultipleHiddenInput()
1065+
)
1066+
type = forms.ChoiceField(
1067+
choices=add_blank_choice(ConsolePortTypeChoices),
1068+
required=False,
1069+
widget=StaticSelect2()
1070+
)
1071+
1072+
class Meta:
1073+
nullable_fields = ('type',)
1074+
1075+
10611076
class ConsoleServerPortTemplateForm(BootstrapMixin, forms.ModelForm):
10621077

10631078
class Meta:
@@ -1086,6 +1101,21 @@ class ConsoleServerPortTemplateCreateForm(BootstrapMixin, forms.Form):
10861101
)
10871102

10881103

1104+
class ConsoleServerPortTemplateBulkEditForm(BootstrapMixin, BulkEditForm):
1105+
pk = forms.ModelMultipleChoiceField(
1106+
queryset=ConsoleServerPortTemplate.objects.all(),
1107+
widget=forms.MultipleHiddenInput()
1108+
)
1109+
type = forms.ChoiceField(
1110+
choices=add_blank_choice(ConsolePortTypeChoices),
1111+
required=False,
1112+
widget=StaticSelect2()
1113+
)
1114+
1115+
class Meta:
1116+
nullable_fields = ('type',)
1117+
1118+
10891119
class PowerPortTemplateForm(BootstrapMixin, forms.ModelForm):
10901120

10911121
class Meta:
@@ -1124,6 +1154,31 @@ class PowerPortTemplateCreateForm(BootstrapMixin, forms.Form):
11241154
)
11251155

11261156

1157+
class PowerPortTemplateBulkEditForm(BootstrapMixin, BulkEditForm):
1158+
pk = forms.ModelMultipleChoiceField(
1159+
queryset=PowerPortTemplate.objects.all(),
1160+
widget=forms.MultipleHiddenInput()
1161+
)
1162+
type = forms.ChoiceField(
1163+
choices=add_blank_choice(PowerPortTypeChoices),
1164+
required=False,
1165+
widget=StaticSelect2()
1166+
)
1167+
maximum_draw = forms.IntegerField(
1168+
min_value=1,
1169+
required=False,
1170+
help_text="Maximum power draw (watts)"
1171+
)
1172+
allocated_draw = forms.IntegerField(
1173+
min_value=1,
1174+
required=False,
1175+
help_text="Allocated power draw (watts)"
1176+
)
1177+
1178+
class Meta:
1179+
nullable_fields = ('type', 'maximum_draw', 'allocated_draw')
1180+
1181+
11271182
class PowerOutletTemplateForm(BootstrapMixin, forms.ModelForm):
11281183

11291184
class Meta:
@@ -1182,6 +1237,26 @@ def __init__(self, *args, **kwargs):
11821237
)
11831238

11841239

1240+
class PowerOutletTemplateBulkEditForm(BootstrapMixin, BulkEditForm):
1241+
pk = forms.ModelMultipleChoiceField(
1242+
queryset=PowerOutletTemplate.objects.all(),
1243+
widget=forms.MultipleHiddenInput()
1244+
)
1245+
type = forms.ChoiceField(
1246+
choices=add_blank_choice(PowerOutletTypeChoices),
1247+
required=False,
1248+
widget=StaticSelect2()
1249+
)
1250+
feed_leg = forms.ChoiceField(
1251+
choices=add_blank_choice(PowerOutletFeedLegChoices),
1252+
required=False,
1253+
widget=StaticSelect2()
1254+
)
1255+
1256+
class Meta:
1257+
nullable_fields = ('type', 'feed_leg')
1258+
1259+
11851260
class InterfaceTemplateForm(BootstrapMixin, forms.ModelForm):
11861261

11871262
class Meta:
@@ -1324,6 +1399,21 @@ def get_iterative_data(self, iteration):
13241399
}
13251400

13261401

1402+
class FrontPortTemplateBulkEditForm(BootstrapMixin, BulkEditForm):
1403+
pk = forms.ModelMultipleChoiceField(
1404+
queryset=FrontPortTemplate.objects.all(),
1405+
widget=forms.MultipleHiddenInput()
1406+
)
1407+
type = forms.ChoiceField(
1408+
choices=add_blank_choice(PortTypeChoices),
1409+
required=False,
1410+
widget=StaticSelect2()
1411+
)
1412+
1413+
class Meta:
1414+
nullable_fields = ()
1415+
1416+
13271417
class RearPortTemplateForm(BootstrapMixin, forms.ModelForm):
13281418

13291419
class Meta:
@@ -1359,6 +1449,21 @@ class RearPortTemplateCreateForm(BootstrapMixin, forms.Form):
13591449
)
13601450

13611451

1452+
class RearPortTemplateBulkEditForm(BootstrapMixin, BulkEditForm):
1453+
pk = forms.ModelMultipleChoiceField(
1454+
queryset=RearPortTemplate.objects.all(),
1455+
widget=forms.MultipleHiddenInput()
1456+
)
1457+
type = forms.ChoiceField(
1458+
choices=add_blank_choice(PortTypeChoices),
1459+
required=False,
1460+
widget=StaticSelect2()
1461+
)
1462+
1463+
class Meta:
1464+
nullable_fields = ()
1465+
1466+
13621467
class DeviceBayTemplateForm(BootstrapMixin, forms.ModelForm):
13631468

13641469
class Meta:
@@ -1383,6 +1488,17 @@ class DeviceBayTemplateCreateForm(BootstrapMixin, forms.Form):
13831488
)
13841489

13851490

1491+
# TODO: DeviceBayTemplate has no fields suitable for bulk-editing yet
1492+
# class DeviceBayTemplateBulkEditForm(BootstrapMixin, BulkEditForm):
1493+
# pk = forms.ModelMultipleChoiceField(
1494+
# queryset=FrontPortTemplate.objects.all(),
1495+
# widget=forms.MultipleHiddenInput()
1496+
# )
1497+
#
1498+
# class Meta:
1499+
# nullable_fields = ()
1500+
1501+
13861502
#
13871503
# Component template import forms
13881504
#

netbox/dcim/tables.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -440,7 +440,7 @@ class ConsoleServerPortTemplateTable(BaseTable):
440440

441441
class Meta(BaseTable.Meta):
442442
model = ConsoleServerPortTemplate
443-
fields = ('pk', 'name', 'actions')
443+
fields = ('pk', 'name', 'type', 'actions')
444444
empty_text = "None"
445445

446446

netbox/dcim/tests/test_views.py

Lines changed: 29 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -537,7 +537,6 @@ class ConsolePortTemplateTestCase(StandardTestCases.Views):
537537
test_create_object = None
538538
test_delete_object = None
539539
test_import_objects = None
540-
test_bulk_edit_objects = None
541540

542541
def test_bulk_create_objects(self):
543542
return self._test_bulk_create_objects(expected_count=3)
@@ -569,6 +568,10 @@ def setUpTestData(cls):
569568
'type': ConsolePortTypeChoices.TYPE_RJ45,
570569
}
571570

571+
cls.bulk_edit_data = {
572+
'type': ConsolePortTypeChoices.TYPE_RJ45,
573+
}
574+
572575

573576
class ConsoleServerPortTemplateTestCase(StandardTestCases.Views):
574577
model = ConsoleServerPortTemplate
@@ -579,7 +582,6 @@ class ConsoleServerPortTemplateTestCase(StandardTestCases.Views):
579582
test_create_object = None
580583
test_delete_object = None
581584
test_import_objects = None
582-
test_bulk_edit_objects = None
583585

584586
def test_bulk_create_objects(self):
585587
return self._test_bulk_create_objects(expected_count=3)
@@ -611,6 +613,10 @@ def setUpTestData(cls):
611613
'type': ConsolePortTypeChoices.TYPE_RJ45,
612614
}
613615

616+
cls.bulk_edit_data = {
617+
'type': ConsolePortTypeChoices.TYPE_RJ45,
618+
}
619+
614620

615621
class PowerPortTemplateTestCase(StandardTestCases.Views):
616622
model = PowerPortTemplate
@@ -621,7 +627,6 @@ class PowerPortTemplateTestCase(StandardTestCases.Views):
621627
test_create_object = None
622628
test_delete_object = None
623629
test_import_objects = None
624-
test_bulk_edit_objects = None
625630

626631
def test_bulk_create_objects(self):
627632
return self._test_bulk_create_objects(expected_count=3)
@@ -645,15 +650,21 @@ def setUpTestData(cls):
645650
'device_type': devicetypes[1].pk,
646651
'name': 'Power Port Template X',
647652
'type': PowerPortTypeChoices.TYPE_IEC_C14,
648-
'maxiumum_draw': 100,
653+
'maximum_draw': 100,
649654
'allocated_draw': 50,
650655
}
651656

652657
cls.bulk_create_data = {
653658
'device_type': devicetypes[1].pk,
654659
'name_pattern': 'Power Port Template [4-6]',
655660
'type': PowerPortTypeChoices.TYPE_IEC_C14,
656-
'maxiumum_draw': 100,
661+
'maximum_draw': 100,
662+
'allocated_draw': 50,
663+
}
664+
665+
cls.bulk_edit_data = {
666+
'type': PowerPortTypeChoices.TYPE_IEC_C14,
667+
'maximum_draw': 100,
657668
'allocated_draw': 50,
658669
}
659670

@@ -667,7 +678,6 @@ class PowerOutletTemplateTestCase(StandardTestCases.Views):
667678
test_create_object = None
668679
test_delete_object = None
669680
test_import_objects = None
670-
test_bulk_edit_objects = None
671681

672682
def test_bulk_create_objects(self):
673683
return self._test_bulk_create_objects(expected_count=3)
@@ -704,6 +714,11 @@ def setUpTestData(cls):
704714
'feed_leg': PowerOutletFeedLegChoices.FEED_LEG_B,
705715
}
706716

717+
cls.bulk_edit_data = {
718+
'type': PowerOutletTypeChoices.TYPE_IEC_C13,
719+
'feed_leg': PowerOutletFeedLegChoices.FEED_LEG_B,
720+
}
721+
707722

708723
class InterfaceTemplateTestCase(StandardTestCases.Views):
709724
model = InterfaceTemplate
@@ -762,7 +777,6 @@ class FrontPortTemplateTestCase(StandardTestCases.Views):
762777
test_create_object = None
763778
test_delete_object = None
764779
test_import_objects = None
765-
test_bulk_edit_objects = None
766780

767781
def test_bulk_create_objects(self):
768782
return self._test_bulk_create_objects(expected_count=3)
@@ -805,6 +819,10 @@ def setUpTestData(cls):
805819
],
806820
}
807821

822+
cls.bulk_edit_data = {
823+
'type': PortTypeChoices.TYPE_8P8C,
824+
}
825+
808826

809827
class RearPortTemplateTestCase(StandardTestCases.Views):
810828
model = RearPortTemplate
@@ -815,7 +833,6 @@ class RearPortTemplateTestCase(StandardTestCases.Views):
815833
test_create_object = None
816834
test_delete_object = None
817835
test_import_objects = None
818-
test_bulk_edit_objects = None
819836

820837
def test_bulk_create_objects(self):
821838
return self._test_bulk_create_objects(expected_count=3)
@@ -849,6 +866,10 @@ def setUpTestData(cls):
849866
'positions': 2,
850867
}
851868

869+
cls.bulk_edit_data = {
870+
'type': PortTypeChoices.TYPE_8P8C,
871+
}
872+
852873

853874
class DeviceBayTemplateTestCase(StandardTestCases.Views):
854875
model = DeviceBayTemplate

netbox/dcim/urls.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,21 +92,25 @@
9292

9393
# Console port templates
9494
path('console-port-templates/add/', views.ConsolePortTemplateCreateView.as_view(), name='consoleporttemplate_add'),
95+
path('console-port-templates/edit/', views.ConsolePortTemplateBulkEditView.as_view(), name='consoleporttemplate_bulk_edit'),
9596
path('console-port-templates/delete/', views.ConsolePortTemplateBulkDeleteView.as_view(), name='consoleporttemplate_bulk_delete'),
9697
path('console-port-templates/<int:pk>/edit/', views.ConsolePortTemplateEditView.as_view(), name='consoleporttemplate_edit'),
9798

9899
# Console server port templates
99100
path('console-server-port-templates/add/', views.ConsoleServerPortTemplateCreateView.as_view(), name='consoleserverporttemplate_add'),
101+
path('console-server-port-templates/edit/', views.ConsoleServerPortTemplateBulkEditView.as_view(), name='consoleserverporttemplate_bulk_edit'),
100102
path('console-server-port-templates/delete/', views.ConsoleServerPortTemplateBulkDeleteView.as_view(), name='consoleserverporttemplate_bulk_delete'),
101103
path('console-server-port-templates/<int:pk>/edit/', views.ConsoleServerPortTemplateEditView.as_view(), name='consoleserverporttemplate_edit'),
102104

103105
# Power port templates
104106
path('power-port-templates/add/', views.PowerPortTemplateCreateView.as_view(), name='powerporttemplate_add'),
107+
path('power-port-templates/edit/', views.PowerPortTemplateBulkEditView.as_view(), name='powerporttemplate_bulk_edit'),
105108
path('power-port-templates/delete/', views.PowerPortTemplateBulkDeleteView.as_view(), name='powerporttemplate_bulk_delete'),
106109
path('power-port-templates/<int:pk>/edit/', views.PowerPortTemplateEditView.as_view(), name='powerporttemplate_edit'),
107110

108111
# Power outlet templates
109112
path('power-outlet-templates/add/', views.PowerOutletTemplateCreateView.as_view(), name='poweroutlettemplate_add'),
113+
path('power-outlet-templates/edit/', views.PowerOutletTemplateBulkEditView.as_view(), name='poweroutlettemplate_bulk_edit'),
110114
path('power-outlet-templates/delete/', views.PowerOutletTemplateBulkDeleteView.as_view(), name='poweroutlettemplate_bulk_delete'),
111115
path('power-outlet-templates/<int:pk>/edit/', views.PowerOutletTemplateEditView.as_view(), name='poweroutlettemplate_edit'),
112116

@@ -118,16 +122,19 @@
118122

119123
# Front port templates
120124
path('front-port-templates/add/', views.FrontPortTemplateCreateView.as_view(), name='frontporttemplate_add'),
125+
path('front-port-templates/edit/', views.FrontPortTemplateBulkEditView.as_view(), name='frontporttemplate_bulk_edit'),
121126
path('front-port-templates/delete/', views.FrontPortTemplateBulkDeleteView.as_view(), name='frontporttemplate_bulk_delete'),
122127
path('front-port-templates/<int:pk>/edit/', views.FrontPortTemplateEditView.as_view(), name='frontporttemplate_edit'),
123128

124129
# Rear port templates
125130
path('rear-port-templates/add/', views.RearPortTemplateCreateView.as_view(), name='rearporttemplate_add'),
131+
path('rear-port-templates/edit/', views.RearPortTemplateBulkEditView.as_view(), name='rearporttemplate_bulk_edit'),
126132
path('rear-port-templates/delete/', views.RearPortTemplateBulkDeleteView.as_view(), name='rearporttemplate_bulk_delete'),
127133
path('rear-port-templates/<int:pk>/edit/', views.RearPortTemplateEditView.as_view(), name='rearporttemplate_edit'),
128134

129135
# Device bay templates
130136
path('device-bay-templates/add/', views.DeviceBayTemplateCreateView.as_view(), name='devicebaytemplate_add'),
137+
# path('device-bay-templates/edit/', views.DeviceBayTemplateBulkEditView.as_view(), name='devicebaytemplate_bulk_edit'),
131138
path('device-bay-templates/delete/', views.DeviceBayTemplateBulkDeleteView.as_view(), name='devicebaytemplate_bulk_delete'),
132139
path('device-bay-templates/<int:pk>/edit/', views.DeviceBayTemplateEditView.as_view(), name='devicebaytemplate_edit'),
133140

0 commit comments

Comments
 (0)