From 493aca453aa94f160764d9a89c8043f7c9a67a78 Mon Sep 17 00:00:00 2001 From: Senko Rasic Date: Sat, 18 May 2013 13:44:27 +0200 Subject: [PATCH 1/2] Fixed #11160 - Ensure full_clean is called from non_form_errors Updated FormSet.non_form_errors() to ensure full_clean() has been called before returning the errors. --- django/forms/formsets.py | 11 +++++++---- tests/forms_tests/tests/test_formsets.py | 14 ++++++++++++++ 2 files changed, 21 insertions(+), 4 deletions(-) diff --git a/django/forms/formsets.py b/django/forms/formsets.py index d421770093c..3ec94d20ec9 100644 --- a/django/forms/formsets.py +++ b/django/forms/formsets.py @@ -250,9 +250,9 @@ class BaseFormSet(object): form -- i.e., from formset.clean(). Returns an empty ErrorList if there are none. """ - if self._non_form_errors is not None: - return self._non_form_errors - return self.error_class() + if self._non_form_errors is None: + self.full_clean() + return self._non_form_errors @property def errors(self): @@ -291,9 +291,12 @@ class BaseFormSet(object): def full_clean(self): """ - Cleans all of self.data and populates self._errors. + Cleans all of self.data and populates self._errors and + self._non_form_errors. """ self._errors = [] + self._non_form_errors = self.error_class() + if not self.is_bound: # Stop further processing. return for i in range(0, self.total_form_count()): diff --git a/tests/forms_tests/tests/test_formsets.py b/tests/forms_tests/tests/test_formsets.py index 4ac3c5ecf15..31adb921ba5 100644 --- a/tests/forms_tests/tests/test_formsets.py +++ b/tests/forms_tests/tests/test_formsets.py @@ -972,6 +972,20 @@ class FormsFormsetTestCase(TestCase): finally: formsets.DEFAULT_MAX_NUM = _old_DEFAULT_MAX_NUM + def test_non_form_errors_run_full_clean(self): + # Regression test for #11160 + # If non_form_errors() is called without calling is_valid() first, + # it should ensure that full_clean() is called. + class BaseCustomFormSet(BaseFormSet): + def clean(self): + raise ValidationError("This is a non-form error") + + ChoiceFormSet = formset_factory(Choice, formset=BaseCustomFormSet) + formset = ChoiceFormSet(data, auto_id=False, prefix='choices') + self.assertTrue(isinstance(formset.non_form_errors(), ErrorList)) + self.assertEqual(list(formset.non_form_errors()), + ['This is a non-form error']) + data = { 'choices-TOTAL_FORMS': '1', # the number of forms rendered From b11b036145d6a1e9d2192e332c8a221a595be1b6 Mon Sep 17 00:00:00 2001 From: Senko Rasic Date: Sat, 18 May 2013 13:49:13 +0200 Subject: [PATCH 2/2] Added myself to AUTHORS --- AUTHORS | 1 + 1 file changed, 1 insertion(+) diff --git a/AUTHORS b/AUTHORS index 3544e3b81ac..e7046f43867 100644 --- a/AUTHORS +++ b/AUTHORS @@ -468,6 +468,7 @@ answer newbie questions, and generally made Django that much better: Luciano Ramalho Amit Ramon Philippe Raoult + Senko Rašić Massimiliano Ravelli Brian Ray Lee Reilly