summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTies Jan Hefting <hello@tiesjan.com>2021-07-07 22:50:30 +0200
committerMariusz Felisiak <felisiak.mariusz@gmail.com>2021-07-15 07:00:39 +0200
commit84400d2e9db7c51fee4e9bb04c028f665b8e7624 (patch)
tree467414be65e1641a02dbcafa575acfff3b09c2b2
parent7f33c1e22dbc34a7afae7967783725b10f1f13b1 (diff)
Fixed #32905 -- Added CSS class for non-form errors of formsets.
-rw-r--r--AUTHORS1
-rw-r--r--django/forms/formsets.py7
-rw-r--r--docs/releases/4.0.txt4
-rw-r--r--docs/topics/forms/formsets.txt14
-rw-r--r--tests/admin_views/tests.py5
-rw-r--r--tests/forms_tests/tests/test_formsets.py14
6 files changed, 42 insertions, 3 deletions
diff --git a/AUTHORS b/AUTHORS
index 4e4dbc00fd..72a87ce7b7 100644
--- a/AUTHORS
+++ b/AUTHORS
@@ -906,6 +906,7 @@ answer newbie questions, and generally made Django that much better:
Thomas Stromberg <tstromberg@google.com>
Thomas Tanner <tanner@gmx.net>
tibimicu@gmx.net
+ Ties Jan Hefting <hello@tiesjan.com>
Tim Allen <tim@pyphilly.org>
Tim Givois <tim.givois.mendez@gmail.com>
Tim Graham <timograham@gmail.com>
diff --git a/django/forms/formsets.py b/django/forms/formsets.py
index a89c35599f..b8e0d62fd9 100644
--- a/django/forms/formsets.py
+++ b/django/forms/formsets.py
@@ -333,7 +333,7 @@ class BaseFormSet:
self._non_form_errors.
"""
self._errors = []
- self._non_form_errors = self.error_class()
+ self._non_form_errors = self.error_class(error_class='nonform')
empty_forms_count = 0
if not self.is_bound: # Stop further processing.
@@ -380,7 +380,10 @@ class BaseFormSet:
# Give self.clean() a chance to do cross-form validation.
self.clean()
except ValidationError as e:
- self._non_form_errors = self.error_class(e.error_list)
+ self._non_form_errors = self.error_class(
+ e.error_list,
+ error_class='nonform'
+ )
def clean(self):
"""
diff --git a/docs/releases/4.0.txt b/docs/releases/4.0.txt
index f0742db7bd..ee3922c9b6 100644
--- a/docs/releases/4.0.txt
+++ b/docs/releases/4.0.txt
@@ -218,6 +218,10 @@ Forms
error message. This allows custom error messages to use the ``%(value)s``
placeholder.
+* :class:`~django.forms.formsets.BaseFormSet` now renders non-form errors with
+ an additional class of ``nonform`` to help distinguish them from
+ form-specific errors.
+
Generic Views
~~~~~~~~~~~~~
diff --git a/docs/topics/forms/formsets.txt b/docs/topics/forms/formsets.txt
index 1f2c7c3fad..0281b6a4d6 100644
--- a/docs/topics/forms/formsets.txt
+++ b/docs/topics/forms/formsets.txt
@@ -365,6 +365,20 @@ The formset ``clean`` method is called after all the ``Form.clean`` methods
have been called. The errors will be found using the ``non_form_errors()``
method on the formset.
+Non-form errors will be rendered with an additional class of ``nonform`` to
+help distinguish them from form-specific errors. For example,
+``{{ formset.non_form_errors }}`` would look like:
+
+.. code-block:: html+django
+
+ <ul class="errorlist nonform">
+ <li>Articles in a set must have distinct titles.</li>
+ </ul>
+
+.. versionchanged:: 4.0
+
+ The additional ``nonform`` class was added.
+
Validating the number of forms in a formset
===========================================
diff --git a/tests/admin_views/tests.py b/tests/admin_views/tests.py
index 880ba0b85f..b277476296 100644
--- a/tests/admin_views/tests.py
+++ b/tests/admin_views/tests.py
@@ -3348,7 +3348,10 @@ class AdminViewListEditable(TestCase):
response = self.client.post(reverse('admin:admin_views_person_changelist'), data)
non_form_errors = response.context['cl'].formset.non_form_errors()
self.assertIsInstance(non_form_errors, ErrorList)
- self.assertEqual(str(non_form_errors), str(ErrorList(["Grace is not a Zombie"])))
+ self.assertEqual(
+ str(non_form_errors),
+ str(ErrorList(['Grace is not a Zombie'], error_class='nonform')),
+ )
def test_list_editable_ordering(self):
collector = Collector.objects.create(id=1, name="Frederick Clegg")
diff --git a/tests/forms_tests/tests/test_formsets.py b/tests/forms_tests/tests/test_formsets.py
index 889560aa74..5afb816f7f 100644
--- a/tests/forms_tests/tests/test_formsets.py
+++ b/tests/forms_tests/tests/test_formsets.py
@@ -337,6 +337,10 @@ class FormsFormsetTestCase(SimpleTestCase):
formset = ChoiceFormSet(data, auto_id=False, prefix='choices')
self.assertFalse(formset.is_valid())
self.assertEqual(formset.non_form_errors(), ['Please submit at most 1 form.'])
+ self.assertEqual(
+ str(formset.non_form_errors()),
+ '<ul class="errorlist nonform"><li>Please submit at most 1 form.</li></ul>',
+ )
def test_formset_validate_min_flag(self):
"""
@@ -359,6 +363,11 @@ class FormsFormsetTestCase(SimpleTestCase):
formset = ChoiceFormSet(data, auto_id=False, prefix='choices')
self.assertFalse(formset.is_valid())
self.assertEqual(formset.non_form_errors(), ['Please submit at least 3 forms.'])
+ self.assertEqual(
+ str(formset.non_form_errors()),
+ '<ul class="errorlist nonform"><li>'
+ 'Please submit at least 3 forms.</li></ul>',
+ )
def test_formset_validate_min_unchanged_forms(self):
"""
@@ -983,6 +992,11 @@ class FormsFormsetTestCase(SimpleTestCase):
formset = FavoriteDrinksFormSet(data, prefix='drinks')
self.assertFalse(formset.is_valid())
self.assertEqual(formset.non_form_errors(), ['You may only specify a drink once.'])
+ self.assertEqual(
+ str(formset.non_form_errors()),
+ '<ul class="errorlist nonform"><li>'
+ 'You may only specify a drink once.</li></ul>',
+ )
def test_formset_iteration(self):
"""Formset instances are iterable."""