From d14bf8c62b66a6f58252d492c2eeca1c952e508c Mon Sep 17 00:00:00 2001 From: Alex Gaynor Date: Sat, 10 Sep 2011 02:42:05 +0000 Subject: [PATCH] Fixed #11404. Added ``FormSet.has_changed``, for consistancy with ``Form.has_changed``. Thanks to michelts for the patch. git-svn-id: http://code.djangoproject.com/svn/django/trunk@16773 bcc190cf-cafb-0310-a4f2-bffc1f526a37 --- django/forms/formsets.py | 6 ++++ docs/topics/forms/formsets.txt | 14 +++++++++ tests/regressiontests/forms/tests/formsets.py | 29 ++++++++++++++++++- 3 files changed, 48 insertions(+), 1 deletion(-) diff --git a/django/forms/formsets.py b/django/forms/formsets.py index ba82978535..04057a18bc 100644 --- a/django/forms/formsets.py +++ b/django/forms/formsets.py @@ -300,6 +300,12 @@ class BaseFormSet(StrAndUnicode): """ pass + def has_changed(self): + """ + Returns true if data in any form differs from initial. + """ + return any(form.has_changed() for form in self) + def add_fields(self, form, index): """A hook for adding extra fields on to each form instance.""" if self.can_order: diff --git a/docs/topics/forms/formsets.txt b/docs/topics/forms/formsets.txt index 50679a1a31..7912294dbb 100644 --- a/docs/topics/forms/formsets.txt +++ b/docs/topics/forms/formsets.txt @@ -150,6 +150,20 @@ As we can see, ``formset.errors`` is a list whose entries correspond to the forms in the formset. Validation was performed for each of the two forms, and the expected error message appears for the second item. +We can also check if form data differs from the initial data (i.e. the form was +sent without any data):: + + >>> data = { + ... 'form-TOTAL_FORMS': u'1', + ... 'form-INITIAL_FORMS': u'0', + ... 'form-MAX_NUM_FORMS': u'', + ... 'form-0-title': u'', + ... 'form-0-pub_date': u'', + ... } + >>> formset = ArticleFormSet(data) + >>> formset.has_changed() + False + .. _understanding-the-managementform: Understanding the ManagementForm diff --git a/tests/regressiontests/forms/tests/formsets.py b/tests/regressiontests/forms/tests/formsets.py index 4ad7e78e7c..f21805334e 100644 --- a/tests/regressiontests/forms/tests/formsets.py +++ b/tests/regressiontests/forms/tests/formsets.py @@ -73,9 +73,11 @@ class FormsFormsetTestCase(TestCase): self.assertTrue(formset.is_valid()) self.assertEqual([form.cleaned_data for form in formset.forms], [{'votes': 100, 'choice': u'Calexico'}]) - # If a FormSet was not passed any data, its is_valid method should return False. + # If a FormSet was not passed any data, its is_valid and has_changed + # methods should return False. formset = ChoiceFormSet() self.assertFalse(formset.is_valid()) + self.assertFalse(formset.has_changed()) def test_formset_validation(self): # FormSet instances can also have an error attribute if validation failed for @@ -93,6 +95,31 @@ class FormsFormsetTestCase(TestCase): self.assertFalse(formset.is_valid()) self.assertEqual(formset.errors, [{'votes': [u'This field is required.']}]) + def test_formset_has_changed(self): + # FormSet instances has_changed method will be True if any data is + # passed to his forms, even if the formset didn't validate + data = { + 'choices-TOTAL_FORMS': '1', # the number of forms rendered + 'choices-INITIAL_FORMS': '0', # the number of forms with initial data + 'choices-MAX_NUM_FORMS': '0', # max number of forms + 'choices-0-choice': '', + 'choices-0-votes': '', + } + blank_formset = ChoiceFormSet(data, auto_id=False, prefix='choices') + self.assertFalse(blank_formset.has_changed()) + + # invalid formset test + data['choices-0-choice'] = 'Calexico' + invalid_formset = ChoiceFormSet(data, auto_id=False, prefix='choices') + self.assertFalse(invalid_formset.is_valid()) + self.assertTrue(invalid_formset.has_changed()) + + # valid formset test + data['choices-0-votes'] = '100' + valid_formset = ChoiceFormSet(data, auto_id=False, prefix='choices') + self.assertTrue(valid_formset.is_valid()) + self.assertTrue(valid_formset.has_changed()) + def test_formset_initial_data(self): # We can also prefill a FormSet with existing data by providing an ``initial`` # argument to the constructor. ``initial`` should be a list of dicts. By default,