Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Dynamically Delete form from model formset django

I have a web page where users can dynamically add and delete forms, to django formsets.

I have read Dynamically adding a form to a Django formset with Ajax, based on which I can add the forms to a formset dynamically. Now I want to delete the formset dynamically. I have looked at Dynamically Delete inline formsets in Django

The way I want to do it is when user clicks on delete, I have an ajax function that deletes the form instance record from the database. When my ajax function returns I keep the same total_form_count and initial_form_count and just the hide the form html so that even the deleted record is submitted in my POST.

Initially the formset renders its form elements as:

#form0
<input id="id_formprefix-0-id" type ="hidden "value="something" name="formprefix-0-id">
<input id="id_formprefix-0-field" value="something" type="text" name="formprefix-0-field">
#form1
<input id="id_formprefix-1-id" type ="hidden "value="something" name="formprefix-1-id">
<input id="id_formprefix-1-field" value="something" type="text" name="formprefix-1-field">
#form2
<input id="id_formprefix-2-id" type ="hidden "value="something" name="formprefix-2-id">
<input id="id_formprefix-2-field" value="something" type="text" name="formprefix-2-field">

Now suppose I dynamically delete form 0, using ajax, after my record is deleted, I do not change the form counts, so the total_form_count and initial_form_count are 3.

( If i reduce the total_form_count and initial_form_count in this case to 2, when I populate the formset in my view using POST data, it is expected to be ordered as form0 and form1. But in my case the valid forms are form1 and form2)

Now in my view, I am do something like the following to save my form.

myformset = modelformset_factory(ObjectElement,form=Object_Form, extra=0, can_delete=True)
for form in myformset.forms:
    print(form.instance.id) #this does not print id of deleted record, but of the first existing record in the database.
    print(form.instance.attribute)# this prints the correct element, which was submitted by POST even for a deleted record.
    try: 
      objectInstance = ObjectElement.objects.get(id = form.instance.id)
      objectInstance.save()
    except ObjectElement.DoesNotExist:
      print("Do not save as the record has already been deleted")     

When I save my formset without deleting any record the saving works ok and the correct form.instance.id is printed. However if I delete a form instance using ajax, and then try to save my formset, the print(form.instance.id) for the deleted record seems to print the first available id in the database, but not the id that was submitted by post. (As the record is deleted , the id is not present in the database, but should it print what was submitted to it via POST right? )

If I do it without the try/catch loop I get the following error by form.errors:

<ul class="errorlist"><li>id<ul class="errorlist"><li>Select a valid choice. That choice is not one of the available choices.</li></ul></li></ul>

which seems to indicate that it is not accepting the correct form ID form the submitted post for the deleted record.

Any body has any idea as to what I am missing here? Or a better way to do this.

How to we go about dynamically deleting forms from formsets, as it seems that the order of the forms should be sequential..

Thanks in Advance!! Any help is appreciated.

like image 556
akotian Avatar asked Sep 13 '12 20:09

akotian


1 Answers

For those who stumble across this issue this could be a possible solution I am able to dynamically delete my form from formset as follows.

So the initial form html looks like

#form0
<input id="id_formprefix-0-id" type ="hidden "value="something" name="formprefix-0-id">
<input id="id_formprefix-0-field" value="something" type="text" name="formprefix-0-field">
#form1
<input id="id_formprefix-1-id" type ="hidden "value="something" name="formprefix-1-id">
<input id="id_formprefix-1-field" value="something" type="text" name="formprefix-1-field">
#form2
<input id="id_formprefix-2-id" type ="hidden "value="something" name="formprefix-2-id">
<input id="id_formprefix-2-field" value="something" type="text" name="formprefix-2-field">

Now suppose I delete form0 and form1 records from my database using ajax. When I submit the form, the formset will not validate, because it expects the forms to be in order, and I only have form 2 remaining in the data base ( I deleted the top two). Formset errors with "Select a valid choice" as described in the question.

So after I delete the forms dynamically, when my ajax returns, I do not change the total_form_count,(https://docs.djangoproject.com/en/1.4/topics/forms/formsets/#understanding-the-managementform) but mark the form as deleted in my html, and just hide the form. Now when the formset is submitted using POST, it also submits the deleted form, but marked as deleted (https://docs.djangoproject.com/en/1.4/topics/forms/formsets/#can-delete)

Now in the view, I first filter out the forms that have been deleted, and only process the forms that still remain as follows

marked_for_delete = formset.deleted_forms
    for form in formset.forms:
        #Filtering out the deleted records, as the formset will not validate, for deleted records
        # if we use form.instance.id or form.initial['id'] below it does not work. 
        #for some reason it returns the id of the first available record in the data base. 
        #form['id'].value(), gives us the id of the deleted element we are looking for
        if form['id'].value() not in [deleted_record['id'].value() for deleted_record in marked_for_delete]:    
            if form.is_valid():
                pass
                # save the form
            else:
                pass
                # error message
like image 163
akotian Avatar answered Nov 03 '22 02:11

akotian