Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Django formset - how to update an object?

How can I update an object from a formset using request.POST?

Here is my code and my problem is that this always creates a new PhoneNumber object. But I want to update the old PhoneNumber object.

def contact_detail(request, contact_id):
    contact = get_object_or_404(Contact, pk=contact_id)
    phone_number_list = PhoneNumber.objects.filter(contact=contact_id)

    if request.method == 'POST':
        cform = ContactForm(request.POST, instance=contact)
        #the next line is probably wrong!
        phonenumberformset = PhoneNumberFormSet(request.POST, queryset=phone_number_list)

        if cform.is_valid() and phonenumberformset.is_valid():
            phonenumber_instances = phonenumberformset.save(commit=False)
            for phonenumber in phonenumber_instances:
                phonenumber.contact = contact
                phonenumber.save()

            request.user.message_set.create(message='The contact "%s" was chanced successfully.' % contact.__str__())
            return HttpResponseRedirect("/crm/contacts/?oby=1")
    else:
        cform = ContactForm(instance=contact)
        phonenumberformset = PhoneNumberFormSet(queryset=phone_number_list)

    return render_to_response(
        'crm/contact_detail.html',
        {'cform': cform, 'phonenumberformset': phonenumberformset,},
        context_instance = RequestContext(request),
    )

Edit: I create three PhoneNumberForms:

PhoneNumberFormSet = modelformset_factory(PhoneNumber, max_num=3, extra=3, exclude=('contact',))

Edit: The solution using inlineformset_factory:

@login_required
def contact_detail(request, contact_id):
    contact = get_object_or_404(Contact, pk=contact_id)
    PhoneNumberInlineFormSet = inlineformset_factory(Contact, PhoneNumber, max_num=3)

    if request.method == 'POST':
        cform = ContactForm(request.POST, instance=contact)
        classificationformset = ClassificationInlineFormSet(request.POST, request.FILES, instance=contact)
        addressformset = AddressInlineFormSet(request.POST, request.FILES, instance=contact)
        phonenumberformset = PhoneNumberInlineFormSet(request.POST, request.FILES, instance=contact)
        if cform.is_valid() and phonenumberformset.is_valid():
            contact = cform.save()
            phonenumberformset.save()

            request.user.message_set.create(message='The contact "%s" was chanced successfully.' % contact.__str__())
            return HttpResponseRedirect("/crm/contacts/?oby=1")
    else:
        cform = ContactForm(instance=contact)
        phonenumberformset = PhoneNumberInlineFormSet(instance=contact)

return render_to_response(
        'crm/contact_detail.html',
        {'cform': cform, 'phonenumberformset': phonenumberformset,},
        context_instance = RequestContext(request),)

This approach even adds a delete checkbox to each inline form. Easy and great.

like image 867
Thomas Kremmel Avatar asked Jan 01 '10 16:01

Thomas Kremmel


1 Answers

Rather than use modelformset_factory, use inlineformset_factory - see the documentation here - sorry, should have pointed you to that initially.

Then you can drop the queryset stuff, since inlineformset_factory takes care of that, and just pass the instance argument (which here refers to the parent model, ie the Contact object). You also won't need to iterate through explicitly setting the contact attribute on save, as again that's taken care of.

like image 81
Daniel Roseman Avatar answered Oct 29 '22 16:10

Daniel Roseman