Ticket #18906: 18906-1.diff

File 18906-1.diff, 5.4 KB (added by claudep, 3 years ago)

Do not validate deleted forms

  • django/forms/formsets.py

    diff --git a/django/forms/formsets.py b/django/forms/formsets.py
    index 42d25fa..0574daa 100644
    a b class BaseFormSet(object): 
    175175    @property
    176176    def deleted_forms(self):
    177177        """
    178         Returns a list of forms that have been marked for deletion. Raises an
    179         AttributeError if deletion is not allowed.
     178        Returns a list of forms that have been marked for deletion.
    180179        """
    181180        if not self.is_valid() or not self.can_delete:
    182             raise AttributeError("'%s' object has no attribute 'deleted_forms'" % self.__class__.__name__)
     181            return []
    183182        # construct _deleted_form_indexes which is just a list of form indexes
    184183        # that have had their deletion widget set to True
    185184        if not hasattr(self, '_deleted_form_indexes'):
  • django/forms/models.py

    diff --git a/django/forms/models.py b/django/forms/models.py
    index 1aa49ea..e0822ed 100644
    a b class BaseModelFormSet(BaseFormSet): 
    500500        # Collect unique_checks and date_checks to run from all the forms.
    501501        all_unique_checks = set()
    502502        all_date_checks = set()
    503         for form in self.forms:
    504             if not form.is_valid():
    505                 continue
     503        forms_to_delete = self.deleted_forms
     504        valid_forms = [form for form in self.forms if form.is_valid() and form not in forms_to_delete]
     505        for form in valid_forms:
    506506            exclude = form._get_validation_exclusions()
    507507            unique_checks, date_checks = form.instance._get_unique_checks(exclude=exclude)
    508508            all_unique_checks = all_unique_checks.union(set(unique_checks))
    class BaseModelFormSet(BaseFormSet): 
    512512        # Do each of the unique checks (unique and unique_together)
    513513        for uclass, unique_check in all_unique_checks:
    514514            seen_data = set()
    515             for form in self.forms:
    516                 if not form.is_valid():
    517                     continue
     515            for form in valid_forms:
    518516                # get data for each field of each of unique_check
    519517                row_data = tuple([form.cleaned_data[field] for field in unique_check if field in form.cleaned_data])
    520518                if row_data and not None in row_data:
    class BaseModelFormSet(BaseFormSet): 
    534532        for date_check in all_date_checks:
    535533            seen_data = set()
    536534            uclass, lookup, field, unique_for = date_check
    537             for form in self.forms:
    538                 if not form.is_valid():
    539                     continue
     535            for form in valid_forms:
    540536                # see if we have data for both fields
    541537                if (form.cleaned_data and form.cleaned_data[field] is not None
    542538                    and form.cleaned_data[unique_for] is not None):
    class BaseModelFormSet(BaseFormSet): 
    591587            return []
    592588
    593589        saved_instances = []
    594         try:
    595             forms_to_delete = self.deleted_forms
    596         except AttributeError:
    597             forms_to_delete = []
     590        forms_to_delete = self.deleted_forms
    598591        for form in self.initial_forms:
    599592            pk_name = self._pk_field.name
    600593            raw_pk_value = form._raw_value(pk_name)
  • tests/modeltests/model_formsets/tests.py

    diff --git a/tests/modeltests/model_formsets/tests.py b/tests/modeltests/model_formsets/tests.py
    index e28560b..f3a8618 100644
    a b class DeletionTests(TestCase): 
    4242        doesn't cause validation errors.
    4343        """
    4444        PoetFormSet = modelformset_factory(Poet, can_delete=True)
     45        poet = Poet.objects.create(name='test')
     46        # One existing untouched and two new unvalid forms
    4547        data = {
    46             'form-TOTAL_FORMS': '1',
    47             'form-INITIAL_FORMS': '0',
     48            'form-TOTAL_FORMS': '3',
     49            'form-INITIAL_FORMS': '1',
    4850            'form-MAX_NUM_FORMS': '0',
    49             'form-0-id': '',
    50             'form-0-name': 'x' * 1000,
     51            'form-0-id': six.text_type(poet.id),
     52            'form-0-name': 'test',
     53            'form-1-id': '',
     54            'form-1-name': 'x' * 1000, # Too long
     55            'form-1-id': six.text_type(poet.id), # Violate unique constraint
     56            'form-1-name': 'test2',
    5157        }
    5258        formset = PoetFormSet(data, queryset=Poet.objects.all())
    5359        # Make sure this form doesn't pass validation.
    5460        self.assertEqual(formset.is_valid(), False)
    55         self.assertEqual(Poet.objects.count(), 0)
     61        self.assertEqual(Poet.objects.count(), 1)
    5662
    5763        # Then make sure that it *does* pass validation and delete the object,
    58         # even though the data isn't actually valid.
     64        # even though the data in new forms aren't actually valid.
    5965        data['form-0-DELETE'] = 'on'
     66        data['form-1-DELETE'] = 'on'
     67        data['form-2-DELETE'] = 'on'
    6068        formset = PoetFormSet(data, queryset=Poet.objects.all())
    6169        self.assertEqual(formset.is_valid(), True)
    6270        formset.save()
    class DeletionTests(TestCase): 
    6472
    6573    def test_change_form_deletion_when_invalid(self):
    6674        """
    67         Make sure that an add form that is filled out, but marked for deletion
     75        Make sure that a change form that is filled out, but marked for deletion
    6876        doesn't cause validation errors.
    6977        """
    7078        PoetFormSet = modelformset_factory(Poet, can_delete=True)
Back to Top