Ticket #14099: patch14099-with-tests.diff

File patch14099-with-tests.diff, 7.0 KB (added by kenth, 5 years ago)

Added tests to #14099

  • django/forms/models.py

    diff --git a/django/forms/models.py b/django/forms/models.py
    index 2a3f8bd..1f946a0 100644
    a b from forms import BaseForm, get_declared_fields 
    1717from fields import Field, ChoiceField
    1818from widgets import SelectMultiple, HiddenInput, MultipleHiddenInput
    1919from widgets import media_property
    20 from formsets import BaseFormSet, formset_factory, DELETION_FIELD_NAME
     20from formsets import BaseFormSet, formset_factory
    2121
    2222__all__ = (
    2323    'ModelForm', 'BaseModelForm', 'model_to_dict', 'fields_for_model',
    class BaseModelFormSet(BaseFormSet): 
    588588            pk_value = getattr(pk_value, 'pk', pk_value)
    589589
    590590            obj = self._existing_object(pk_value)
    591             if self.can_delete:
    592                 raw_delete_value = form._raw_value(DELETION_FIELD_NAME)
    593                 should_delete = form.fields[DELETION_FIELD_NAME].clean(raw_delete_value)
    594                 if should_delete:
    595                     self.deleted_objects.append(obj)
    596                     obj.delete()
    597                     continue
     591            if self.can_delete and self._should_delete_form(form):
     592                self.deleted_objects.append(obj)
     593                obj.delete()
     594                continue
    598595            if form.has_changed():
    599596                self.changed_objects.append((obj, form.changed_data))
    600597                saved_instances.append(self.save_existing(form, obj, commit=commit))
    class BaseModelFormSet(BaseFormSet): 
    609606                continue
    610607            # If someone has marked an add form for deletion, don't save the
    611608            # object.
    612             if self.can_delete:
    613                 raw_delete_value = form._raw_value(DELETION_FIELD_NAME)
    614                 should_delete = form.fields[DELETION_FIELD_NAME].clean(raw_delete_value)
    615                 if should_delete:
    616                     continue
     609            if self.can_delete and self._should_delete_form(form):
     610                continue
    617611            self.new_objects.append(self.save_new(form, commit=commit))
    618612            if not commit:
    619613                self.saved_forms.append(form)
  • tests/regressiontests/model_formsets_regress/tests.py

    diff --git a/tests/regressiontests/model_formsets_regress/tests.py b/tests/regressiontests/model_formsets_regress/tests.py
    index 3354fc5..368c5b0 100644
    a b class FormfieldCallbackTests(TestCase): 
    283283        modelformset_factory(UserSite, form=UserSiteForm,
    284284                             formfield_callback=callback)
    285285        self.assertCallbackCalled(callback)
     286
     287
     288# mixin class for #14099 regression
     289from django.forms.formsets import BaseFormSet, DELETION_FIELD_NAME
     290from django.forms.models import BaseModelFormSet
     291
     292class BaseCustomDeleteFormSet(BaseFormSet):
     293    """
     294    A formset mix-in that lets a form decide if it's to be deleted
     295    Works for BaseFormSets. Also works for ModelFormSets with #14099
     296
     297    form.should_delete() is called. The formset delete field is also suppressed.
     298    """
     299    def add_fields(self, form, index):
     300        super(BaseCustomDeleteFormSet, self).add_fields(form, index)
     301        self.can_delete = True
     302        if DELETION_FIELD_NAME in form.fields:
     303            del form.fields[DELETION_FIELD_NAME]
     304
     305    def _should_delete_form(self, form):
     306        return hasattr(form, 'should_delete') and form.should_delete()
     307
     308
     309class FormfieldShouldDeleteFormTests(TestCase):
     310    """
     311    Regression for #14099: BaseModelFormSet should use ModelFormSet method _should_delete_form
     312    """
     313
     314    class BaseCustomDeleteModelFormSet(BaseModelFormSet, BaseCustomDeleteFormSet):
     315        """ Model FormSet with CustomDelete MixIn """
     316
     317    class CustomDeleteUserForm(forms.ModelForm):
     318        """ A model form with a 'should_delete' method """
     319        class Meta:
     320            model = User
     321
     322        def should_delete(self):
     323            """ delete form if odd PK """
     324            return self.instance.id % 2
     325
     326    Normal_Formset = modelformset_factory(User, form=CustomDeleteUserForm, can_delete=True)
     327    Delete_Formset = modelformset_factory(User, form=CustomDeleteUserForm, formset=BaseCustomDeleteModelFormSet)
     328
     329    data = {
     330            'form-TOTAL_FORMS': '4',
     331            'form-INITIAL_FORMS': '0',
     332            'form-MAX_NUM_FORMS': '4',
     333            'form-0-username': 'John',
     334            'form-0-serial': '1',
     335            'form-1-username': 'Paul',
     336            'form-1-serial': '2',
     337            'form-2-username': 'George',
     338            'form-2-serial': '3',
     339            'form-3-username': 'Ringo',
     340            'form-3-serial': '5',
     341            }
     342
     343    bound_ids = {
     344            'form-INITIAL_FORMS': '4',
     345            'form-0-id': '1',
     346            'form-1-id': '2',
     347            'form-2-id': '3',
     348            'form-3-id': '4',
     349            }
     350
     351    delete_all_ids = {
     352            'form-0-DELETE': '1',
     353            'form-1-DELETE': '1',
     354            'form-2-DELETE': '1',
     355            'form-3-DELETE': '1',
     356            }
     357
     358    def test_init_database(self):
     359        """ Add test data to database via formset """
     360        formset = self.Normal_Formset(self.data)
     361        self.assertTrue(formset.is_valid())
     362        self.assertEqual(len(formset.save()), 4)
     363
     364    def test_no_delete(self):
     365        """ Verify base formset doesn't modify database """
     366        # reload database
     367        self.test_init_database()
     368
     369        # pass standard data dict & see none updated
     370        data = dict(self.data)
     371        data.update(self.bound_ids)
     372        formset = self.Normal_Formset(data, queryset=User.objects.all())
     373        self.assertTrue(formset.is_valid())
     374        self.assertEqual(len(formset.save()), 0)
     375        self.assertEqual(len(User.objects.all()), 4)
     376
     377    def test_all_delete(self):
     378        """ Verify base formset honors DELETE field """
     379        # reload database
     380        self.test_init_database()
     381
     382        # create data dict with all fields marked for deletion
     383        data = dict(self.data)
     384        data.update(self.bound_ids)
     385        data.update(self.delete_all_ids)
     386        formset = self.Normal_Formset(data, queryset=User.objects.all())
     387        self.assertTrue(formset.is_valid())
     388        self.assertEqual(len(formset.save()), 0)
     389        self.assertEqual(len(User.objects.all()), 0)
     390
     391    def test_custom_delete(self):
     392        """ Verify custom_dete formset ignores DELETE field & uses form method """
     393        # reload database
     394        self.test_init_database()
     395
     396        # Create formset with custom Delete function
     397        # create data dict with all fields marked for deletion
     398        data = dict(self.data)
     399        data.update(self.bound_ids)
     400        data.update(self.delete_all_ids)
     401        formset = self.Delete_Formset(data, queryset=User.objects.all())
     402
     403        # verify two were deleted
     404        self.assertTrue(formset.is_valid())
     405        self.assertEqual(len(formset.save()), 0)
     406        self.assertEqual(len(User.objects.all()), 2)
     407
     408        # verify no "odd" PKs left
     409        odd_ids = [user.id for user in User.objects.all() if user.id % 2]
     410        self.assertEqual(len(odd_ids), 0)
Back to Top