Fixed #10284 -- ModelFormSet.save(commit=False) no longer deletes objects

Thanks laureline.guerin@ and Wedg.
This commit is contained in:
Tim Graham 2013-07-12 09:52:18 -04:00
parent 311c1d2848
commit 65e03a424e
5 changed files with 43 additions and 9 deletions

View File

@ -717,6 +717,7 @@ class BaseModelFormSet(BaseFormSet):
obj = self._existing_object(pk_value) obj = self._existing_object(pk_value)
if form in forms_to_delete: if form in forms_to_delete:
self.deleted_objects.append(obj) self.deleted_objects.append(obj)
if commit:
obj.delete() obj.delete()
continue continue
if form.has_changed(): if form.has_changed():

View File

@ -94,6 +94,11 @@ Miscellaneous
have a custom :class:`~django.core.files.uploadhandler.FileUploadHandler` have a custom :class:`~django.core.files.uploadhandler.FileUploadHandler`
that implements ``new_file()``, be sure it accepts this new parameter. that implements ``new_file()``, be sure it accepts this new parameter.
* :class:`ModelFormSet<django.forms.models.BaseModelFormSet>`'s no longer
delete instances when ``save(commit=False)`` is called. See
:attr:`~django.forms.formsets.BaseFormSet.can_delete` for instructions on how
to manually delete objects from deleted forms.
Features deprecated in 1.7 Features deprecated in 1.7
========================== ==========================

View File

@ -3,7 +3,10 @@
Formsets Formsets
======== ========
.. class:: django.forms.formsets.BaseFormSet .. module:: django.forms.formsets
:synopsis: An abstraction for working with multiple forms on the same page.
.. class:: BaseFormSet
A formset is a layer of abstraction to work with multiple forms on the same A formset is a layer of abstraction to work with multiple forms on the same
page. It can be best compared to a data grid. Let's say you have the following page. It can be best compared to a data grid. Let's say you have the following
@ -164,9 +167,7 @@ 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 forms in the formset. Validation was performed for each of the two forms, and
the expected error message appears for the second item. the expected error message appears for the second item.
.. currentmodule:: django.forms.formsets.BaseFormSet .. method:: BaseFormSet.total_error_count(self)
.. method:: total_error_count(self)
.. versionadded:: 1.6 .. versionadded:: 1.6
@ -353,6 +354,8 @@ formsets and deletion of forms from a formset.
``can_order`` ``can_order``
~~~~~~~~~~~~~ ~~~~~~~~~~~~~
.. attribute:: BaseFormSet.can_order
Default: ``False`` Default: ``False``
Lets you create a formset with the ability to order:: Lets you create a formset with the ability to order::
@ -411,6 +414,8 @@ happen when the user changes these values::
``can_delete`` ``can_delete``
~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~
.. attribute:: BaseFormSet.can_delete
Default: ``False`` Default: ``False``
Lets you create a formset with the ability to select forms for deletion:: Lets you create a formset with the ability to select forms for deletion::
@ -463,10 +468,23 @@ delete fields you can access them with ``deleted_forms``::
If you are using a :class:`ModelFormSet<django.forms.models.BaseModelFormSet>`, If you are using a :class:`ModelFormSet<django.forms.models.BaseModelFormSet>`,
model instances for deleted forms will be deleted when you call model instances for deleted forms will be deleted when you call
``formset.save()``. On the other hand, if you are using a plain ``FormSet``, ``formset.save()``.
it's up to you to handle ``formset.deleted_forms``, perhaps in your formset's
``save()`` method, as there's no general notion of what it means to delete a .. versionchanged:: 1.7
form.
If you call ``formset.save(commit=False)``, objects will not be deleted
automatically. You'll need to call ``delete()`` on each of the
:attr:`formset.deleted_objects
<django.forms.models.BaseModelFormSet.deleted_objects>` to actually delete
them::
>>> instances = formset.save(commit=False)
>>> for obj in formset.deleted_objects:
... obj.delete()
On the other hand, if you are using a plain ``FormSet``, it's up to you to
handle ``formset.deleted_forms``, perhaps in your formset's ``save()`` method,
as there's no general notion of what it means to delete a form.
Adding additional fields to a formset Adding additional fields to a formset
------------------------------------- -------------------------------------

View File

@ -825,6 +825,13 @@ to the database. If your formset contains a ``ManyToManyField``, you'll also
need to call ``formset.save_m2m()`` to ensure the many-to-many relationships need to call ``formset.save_m2m()`` to ensure the many-to-many relationships
are saved properly. are saved properly.
After calling ``save()``, your model formset will have three new attributes
containing the formset's changes:
.. attribute:: models.BaseModelFormSet.changed_objects
.. attribute:: models.BaseModelFormSet.deleted_objects
.. attribute:: models.BaseModelFormSet.new_objects
.. _model-formsets-max-num: .. _model-formsets-max-num:
Limiting the number of editable objects Limiting the number of editable objects

View File

@ -32,6 +32,9 @@ class DeletionTests(TestCase):
'form-0-DELETE': 'on', 'form-0-DELETE': 'on',
} }
formset = PoetFormSet(data, queryset=Poet.objects.all()) formset = PoetFormSet(data, queryset=Poet.objects.all())
formset.save(commit=False)
self.assertEqual(Poet.objects.count(), 1)
formset.save() formset.save()
self.assertTrue(formset.is_valid()) self.assertTrue(formset.is_valid())
self.assertEqual(Poet.objects.count(), 0) self.assertEqual(Poet.objects.count(), 0)