I can't seem to find a good way to solve this situation.
I have two models Office
and People
with a many to many relationship through a Contact
model with additional fields.
Now in my views
(CreateView and UpdateView), I am using inline formset to manage the relationship.
My problem here is with UpdateView
, how do I update the many to many relationship? I can add new items. But how to delete existing ones? The formset generates a checkbox DELETE
but I get lost in the code. How to use it?
One way could be to delete all the corresponding rows in the through model and recreates new ones with data submitted from the form but I believe there should be a more efficient way to do it.
Can someone help?
Here's my current code:
def post(self, request, *args, **kwargs):
self.object = self.get_object()
form_class = self.get_form_class()
form = self.get_form(form_class)
formset = OfficeContactInline(request.POST, instance=self.object)
if form.is_valid() and formset.is_valid():
self.object = form.save()
contacts = formset.save(commit=False)
for contact in contacts:
contact.office = self.object
contact.save()
formset.save_m2m()
return HttpResponseRedirect(self.get_success_url())
else:
return render(request, self.template_name, self.get_context_data(form=form, formset=formset))
I finally found a solution to my problem. There actually is a change in behavior with Django 1.7: formset.save(commit=False)
no longer deletes deleted items (checkbox checked).
You therefore have to use formset.deleted_objects
to do it: working code below
def post(self, request, *args, **kwargs):
self.object = self.get_object()
form_class = self.get_form_class()
form = self.get_form(form_class)
formset = OfficeContactInline(request.POST, instance=self.object)
if form.is_valid() and formset.is_valid():
self.object = form.save()
contacts = formset.save(commit=False)
# New with Django 1.7
for del_contact in formset.deleted_objects:
del_contact.delete()
for contact in contacts:
contact.office = self.object
contact.save()
formset.save_m2m()
return HttpResponseRedirect(self.get_success_url())
else:
return render(request, self.template_name, self.get_context_data(form=form, formset=formset))
This is documented here: https://docs.djangoproject.com/en/1.7/topics/forms/formsets/#django.forms.formsets.BaseFormSet.can_delete