summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBaptiste Mispelon <bmispelon@gmail.com>2021-12-11 20:17:29 +0100
committerMariusz Felisiak <felisiak.mariusz@gmail.com>2021-12-21 12:06:05 +0100
commite95e6425ac354b8fb0c24580b740dbdc2ea72bd6 (patch)
treeabdadc46924748086d6788364f747abb76551029
parent61b332499d28afe1c7d413411166ad405663e750 (diff)
Refs #24121 -- Added __repr__() to BaseFormSet.
-rw-r--r--django/forms/formsets.py16
-rw-r--r--tests/forms_tests/tests/test_formsets.py63
2 files changed, 79 insertions, 0 deletions
diff --git a/django/forms/formsets.py b/django/forms/formsets.py
index aca518b4d2..75b0646512 100644
--- a/django/forms/formsets.py
+++ b/django/forms/formsets.py
@@ -103,6 +103,22 @@ class BaseFormSet(RenderableFormMixin):
"""
return True
+ def __repr__(self):
+ if self._errors is None:
+ is_valid = 'Unknown'
+ else:
+ is_valid = (
+ self.is_bound and
+ not self._non_form_errors and
+ not any(form_errors for form_errors in self._errors)
+ )
+ return '<%s: bound=%s valid=%s total_forms=%s>' % (
+ self.__class__.__qualname__,
+ self.is_bound,
+ is_valid,
+ self.total_form_count(),
+ )
+
@cached_property
def management_form(self):
"""Return the ManagementForm instance for this FormSet."""
diff --git a/tests/forms_tests/tests/test_formsets.py b/tests/forms_tests/tests/test_formsets.py
index 1705d8bb6b..50342cfaa9 100644
--- a/tests/forms_tests/tests/test_formsets.py
+++ b/tests/forms_tests/tests/test_formsets.py
@@ -25,6 +25,12 @@ class Choice(Form):
ChoiceFormSet = formset_factory(Choice)
+class ChoiceFormsetWithNonFormError(ChoiceFormSet):
+ def clean(self):
+ super().clean()
+ raise ValidationError('non-form error')
+
+
class FavoriteDrinkForm(Form):
name = CharField()
@@ -1328,6 +1334,63 @@ class FormsFormsetTestCase(SimpleTestCase):
self.assertEqual(formset.non_form_errors().renderer, renderer)
self.assertEqual(formset.empty_form.renderer, renderer)
+ def test_repr(self):
+ valid_formset = self.make_choiceformset([('test', 1)])
+ valid_formset.full_clean()
+ invalid_formset = self.make_choiceformset([('test', '')])
+ invalid_formset.full_clean()
+ partially_invalid_formset = self.make_choiceformset(
+ [('test', '1'), ('test', '')],
+ )
+ partially_invalid_formset.full_clean()
+ invalid_formset_non_form_errors_only = self.make_choiceformset(
+ [('test', '')],
+ formset_class=ChoiceFormsetWithNonFormError,
+ )
+ invalid_formset_non_form_errors_only.full_clean()
+
+ cases = [
+ (
+ self.make_choiceformset(),
+ '<ChoiceFormSet: bound=False valid=Unknown total_forms=1>',
+ ),
+ (
+ self.make_choiceformset(
+ formset_class=formset_factory(Choice, extra=10),
+ ),
+ '<ChoiceFormSet: bound=False valid=Unknown total_forms=10>',
+ ),
+ (
+ self.make_choiceformset([]),
+ '<ChoiceFormSet: bound=True valid=Unknown total_forms=0>',
+ ),
+ (
+ self.make_choiceformset([('test', 1)]),
+ '<ChoiceFormSet: bound=True valid=Unknown total_forms=1>',
+ ),
+ (valid_formset, '<ChoiceFormSet: bound=True valid=True total_forms=1>'),
+ (invalid_formset, '<ChoiceFormSet: bound=True valid=False total_forms=1>'),
+ (
+ partially_invalid_formset,
+ '<ChoiceFormSet: bound=True valid=False total_forms=2>',
+ ),
+ (
+ invalid_formset_non_form_errors_only,
+ '<ChoiceFormsetWithNonFormError: bound=True valid=False total_forms=1>',
+ ),
+ ]
+ for formset, expected_repr in cases:
+ with self.subTest(expected_repr=expected_repr):
+ self.assertEqual(repr(formset), expected_repr)
+
+ def test_repr_do_not_trigger_validation(self):
+ formset = self.make_choiceformset([('test', 1)])
+ with mock.patch.object(formset, 'full_clean') as mocked_full_clean:
+ repr(formset)
+ mocked_full_clean.assert_not_called()
+ formset.is_valid()
+ mocked_full_clean.assert_called()
+
@jinja2_tests
class Jinja2FormsFormsetTestCase(FormsFormsetTestCase):