From 01b0eb50fd4a3503a80524f5fd783b0dba038105 Mon Sep 17 00:00:00 2001 From: Alex Gaynor Date: Sat, 10 Sep 2011 01:53:56 +0000 Subject: [PATCH] Make ``Formset.__getitem__`` O(1), rather than O(n). If you override ``__iter__`` you now need to also override ``__getitem__`` for consistant behavior. Thanks to Carl and Russ for the review. git-svn-id: http://code.djangoproject.com/svn/django/trunk@16770 bcc190cf-cafb-0310-a4f2-bffc1f526a37 --- django/forms/formsets.py | 2 +- docs/topics/forms/formsets.txt | 4 ++++ tests/regressiontests/forms/tests/formsets.py | 20 ++++++++++--------- 3 files changed, 16 insertions(+), 10 deletions(-) diff --git a/django/forms/formsets.py b/django/forms/formsets.py index 6f4783116d..ba82978535 100644 --- a/django/forms/formsets.py +++ b/django/forms/formsets.py @@ -55,7 +55,7 @@ class BaseFormSet(StrAndUnicode): def __getitem__(self, index): """Returns the form at the given index, based on the rendering order""" - return list(self)[index] + return self.forms[index] def __len__(self): return len(self.forms) diff --git a/docs/topics/forms/formsets.txt b/docs/topics/forms/formsets.txt index a72fac947a..50679a1a31 100644 --- a/docs/topics/forms/formsets.txt +++ b/docs/topics/forms/formsets.txt @@ -49,6 +49,10 @@ they were created. The default formset iterator also renders the forms in this order, but you can change this order by providing an alternate implementation for the :meth:`__iter__()` method. +Formsets can also be indexed into, which returns the corresponding form. If you +override ``__iter__``, you will need to also override ``__getitem__`` to have +matching behavior. + Using initial data with a formset --------------------------------- diff --git a/tests/regressiontests/forms/tests/formsets.py b/tests/regressiontests/forms/tests/formsets.py index 869b6e35a6..4ad7e78e7c 100644 --- a/tests/regressiontests/forms/tests/formsets.py +++ b/tests/regressiontests/forms/tests/formsets.py @@ -793,8 +793,10 @@ class FormsFormsetTestCase(TestCase): # Formets can override the default iteration order class BaseReverseFormSet(BaseFormSet): def __iter__(self): - for form in reversed(self.forms): - yield form + return reversed(self.forms) + + def __getitem__(self, idx): + return super(BaseReverseFormSet, self).__getitem__(len(self) - idx - 1) ReverseChoiceFormset = formset_factory(Choice, BaseReverseFormSet, extra=3) reverse_formset = ReverseChoiceFormset() @@ -911,12 +913,12 @@ class TestIsBoundBehavior(TestCase): # The empty forms should be equal. self.assertEqual(empty_forms[0].as_p(), empty_forms[1].as_p()) -class TestEmptyFormSet(TestCase): +class TestEmptyFormSet(TestCase): "Test that an empty formset still calls clean()" - def test_empty_formset_is_valid(self): - EmptyFsetWontValidateFormset = formset_factory(FavoriteDrinkForm, extra=0, formset=EmptyFsetWontValidate) - formset = EmptyFsetWontValidateFormset(data={'form-INITIAL_FORMS':'0', 'form-TOTAL_FORMS':'0'},prefix="form") - formset2 = EmptyFsetWontValidateFormset(data={'form-INITIAL_FORMS':'0', 'form-TOTAL_FORMS':'1', 'form-0-name':'bah' },prefix="form") - self.assertFalse(formset.is_valid()) - self.assertFalse(formset2.is_valid()) + def test_empty_formset_is_valid(self): + EmptyFsetWontValidateFormset = formset_factory(FavoriteDrinkForm, extra=0, formset=EmptyFsetWontValidate) + formset = EmptyFsetWontValidateFormset(data={'form-INITIAL_FORMS':'0', 'form-TOTAL_FORMS':'0'},prefix="form") + formset2 = EmptyFsetWontValidateFormset(data={'form-INITIAL_FORMS':'0', 'form-TOTAL_FORMS':'1', 'form-0-name':'bah' },prefix="form") + self.assertFalse(formset.is_valid()) + self.assertFalse(formset2.is_valid())