diff --git a/docs/additional-features/custom-scripts.md b/docs/additional-features/custom-scripts.md index 496e4f7fa..a2b4191ec 100644 --- a/docs/additional-features/custom-scripts.md +++ b/docs/additional-features/custom-scripts.md @@ -154,6 +154,10 @@ CHOICES = ( direction = ChoiceVar(choices=CHOICES) ``` +### MultiChoiceVar + +Similar to `ChoiceVar`, but allows for the selection of multiple choices. + ### ObjectVar A NetBox object of a particular type, identified by the associated queryset. Most models will utilize the REST API to retrieve available options: Note that any filtering on the queryset in this case has no effect. diff --git a/docs/release-notes/version-2.8.md b/docs/release-notes/version-2.8.md index af758f928..6b41ead7c 100644 --- a/docs/release-notes/version-2.8.md +++ b/docs/release-notes/version-2.8.md @@ -1,5 +1,13 @@ # NetBox v2.8 +## v2.8.10 (FUTURE) + +### Enhancements + +* [#4885](https://github.com/netbox-community/netbox/issues/4885) - Add MultiChoiceVar for custom scripts + +--- + ## v2.8.9 (2020-08-04) ### Enhancements diff --git a/netbox/extras/scripts.py b/netbox/extras/scripts.py index 82e955b8a..6876ce10b 100644 --- a/netbox/extras/scripts.py +++ b/netbox/extras/scripts.py @@ -32,6 +32,7 @@ __all__ = [ 'IPAddressVar', 'IPAddressWithMaskVar', 'IPNetworkVar', + 'MultiChoiceVar', 'MultiObjectVar', 'ObjectVar', 'Script', @@ -165,6 +166,13 @@ class ChoiceVar(ScriptVariable): self.field_attrs['choices'] = choices +class MultiChoiceVar(ChoiceVar): + """ + Like ChoiceVar, but allows for the selection of multiple choices. + """ + form_field = forms.MultipleChoiceField + + class ObjectVar(ScriptVariable): """ A single object within NetBox. diff --git a/netbox/extras/tests/test_scripts.py b/netbox/extras/tests/test_scripts.py index 6237d1d95..482b86b9a 100644 --- a/netbox/extras/tests/test_scripts.py +++ b/netbox/extras/tests/test_scripts.py @@ -5,6 +5,12 @@ from netaddr import IPAddress, IPNetwork from dcim.models import DeviceRole from extras.scripts import * +CHOICES = ( + ('ff0000', 'Red'), + ('00ff00', 'Green'), + ('0000ff', 'Blue') +) + class ScriptVariablesTest(TestCase): @@ -101,12 +107,6 @@ class ScriptVariablesTest(TestCase): def test_choicevar(self): - CHOICES = ( - ('ff0000', 'Red'), - ('00ff00', 'Green'), - ('0000ff', 'Blue') - ) - class TestScript(Script): var1 = ChoiceVar( @@ -114,12 +114,37 @@ class ScriptVariablesTest(TestCase): ) # Validate valid choice - data = {'var1': CHOICES[0][0]} + data = {'var1': 'ff0000'} form = TestScript().as_form(data) self.assertTrue(form.is_valid()) - self.assertEqual(form.cleaned_data['var1'], CHOICES[0][0]) + self.assertEqual(form.cleaned_data['var1'], 'ff0000') - # Validate invalid choices + # Validate invalid choice + data = {'var1': 'taupe'} + form = TestScript().as_form(data) + self.assertFalse(form.is_valid()) + + def test_multichoicevar(self): + + class TestScript(Script): + + var1 = MultiChoiceVar( + choices=CHOICES + ) + + # Validate single choice + data = {'var1': ['ff0000']} + form = TestScript().as_form(data) + self.assertTrue(form.is_valid()) + self.assertEqual(form.cleaned_data['var1'], ['ff0000']) + + # Validate multiple choices + data = {'var1': ('ff0000', '00ff00')} + form = TestScript().as_form(data) + self.assertTrue(form.is_valid()) + self.assertEqual(form.cleaned_data['var1'], ['ff0000', '00ff00']) + + # Validate invalid choice data = {'var1': 'taupe'} form = TestScript().as_form(data) self.assertFalse(form.is_valid())