When using modelformset_factory how do you delete objects from the database that get marked for delete in the form?
I create my modelformset_factory like this:
ItemFormset = modelformset_factory(Item, ItemModelForm, extra=1, can_delete=True)
qset = Item.objects.filter(pr=pr)
formset = ItemFormset(queryset=qset)
When the formset comes back in the POST I get the data like so:
if request.method == "POST":
formset = ItemFormset(request.POST,queryset=qset)
if formset.is_valid():
marked_for_delete = formset.deleted_forms
instances = formset.save(commit=False)
for item in instances:
item.pr = pr
item.save()
When the formset comes back I can get all of the objects marked for delete with formset.deleted_forms
but I can't figure out how to actually delete them. I've tried looping through each one and deleting each one individually but I get the error: Item object can't be deleted because its id attribute is set to None.
In the template I'm including {{form.id}}
so each object has it's ID being passed back in the POST.
After calling instances = formset.save(commit=False)
I can call formset.deleted_objects
but it's just an empty list: []
Can anyone see what I'm doing wrong that would make the objects not get deleted from the database?
What is confusing you is that formset.save(commit=False)
doesn't do what you think it does.
Although with commit=False
set, edited objects are not save()
d, confusingly, deleted objects are deleted.
Therefore, when you loop over marked_for_delete
after having called save(commit=False)
, you're getting objects that have been deleted already, hence the None
for their id's.
Your self-answer is better, more idiomatic Django as it happens; in general, one should just call formset.save()
and let it default to commit=True
. The fact that the commit=False
case is relatively rare and disused is probably why nobody has fixed the (IMO, buggy) behavior of deleting objects.
(As an aside, I have only observed this behavior in non-transactional/AutoCommit database environments; it might be that with commit=False
and transactions enabled you get a more robust behavior with respect to deletion.)
P.S. - This behavior has been changed in Django 1.7:
"If you call formset.save(commit=False), objects will not be deleted automatically. You’ll need to call delete() on each of the formset.deleted_objects to actually delete them."
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With