views:

338

answers:

1

Hi,

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.

+2  A: 

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.

Daniel Roseman
Thanks..I learn a lot today :) I get another error also with this approach: IndexError at /crm/contacts/15/- list index out of range .. hopefully I get rid of it with inlineformset_factory
Tom Tom
Ok seems to work. I just tried it for the PhoneNumber objects. And wow.. I got a really nice delete checkbox for each inline object.. wuhaa :) But one question: Is there a way to define a max_num of inline objects? Since it always adds three more inlines.
Tom Tom
Ah ok, same solution as for "normal" formsets. max_num=3 or extra=0
Tom Tom