From 5fbae8407e408da2e7ed7074e87b0424e4969284 Mon Sep 17 00:00:00 2001 From: Jason Novinger Date: Tue, 21 Oct 2025 11:54:46 -0500 Subject: [PATCH] Only show non-rendered field errors in toast When script form validation fails, display error messages for fields not in fieldsets. Fields in fieldsets show inline errors only; hidden fields show toast notifications to provide feedback instead of failing silently. --- netbox/extras/tests/test_views.py | 27 ++++++++++++++++++++++++++- netbox/extras/views.py | 12 ++++++++---- 2 files changed, 34 insertions(+), 5 deletions(-) diff --git a/netbox/extras/tests/test_views.py b/netbox/extras/tests/test_views.py index 145606393..91444e2ce 100644 --- a/netbox/extras/tests/test_views.py +++ b/netbox/extras/tests/test_views.py @@ -930,7 +930,6 @@ class ScriptValidationErrorTest(TestCase): @tag('regression') def test_script_validation_error_displays_message(self): - """Test that form validation errors are displayed to the user""" from unittest.mock import patch url = reverse('extras:script', kwargs={'pk': self.script.pk}) @@ -942,3 +941,29 @@ class ScriptValidationErrorTest(TestCase): messages = list(response.context['messages']) self.assertEqual(len(messages), 1) self.assertEqual(str(messages[0]), "bar: This field is required.") + + @tag('regression') + def test_script_validation_error_no_toast_for_fieldset_fields(self): + from unittest.mock import patch, PropertyMock + + class FieldsetScript(PythonClass): + class Meta: + name = 'Fieldset test' + commit_default = False + fieldsets = (("Fields", ("required_field",)),) + + required_field = IntegerVar(min_value=10) + + def run(self, data, commit): + return "Complete" + + url = reverse('extras:script', kwargs={'pk': self.script.pk}) + + with patch.object(Script, 'python_class', new_callable=PropertyMock) as mock_python_class: + mock_python_class.return_value = FieldsetScript + with patch('extras.views.get_workers_for_queue', return_value=['worker']): + response = self.client.post(url, {'required_field': '5', '_commit': 'true'}) + + self.assertEqual(response.status_code, 200) + messages = list(response.context['messages']) + self.assertEqual(len(messages), 0) diff --git a/netbox/extras/views.py b/netbox/extras/views.py index b3eb435d6..32f87fb97 100644 --- a/netbox/extras/views.py +++ b/netbox/extras/views.py @@ -1486,10 +1486,14 @@ class ScriptView(BaseScriptView): return redirect('extras:script_result', job_pk=job.pk) else: - messages.error( - request, - '; '.join(f"{field}: {', '.join(errors)}" for field, errors in form.errors.items()) - ) + fieldset_fields = {field for _, fields in script_class.get_fieldsets() for field in fields} + hidden_errors = { + field: errors for field, errors in form.errors.items() + if field not in fieldset_fields + } + if hidden_errors: + error_msg = '; '.join(f"{field}: {', '.join(errors)}" for field, errors in hidden_errors.items()) + messages.error(request, error_msg) return render(request, 'extras/script.html', { 'object': script,