How to Use Django Formset in Web Applications

by Ather Rashid

Django provides a unique way of capturing data from client side and processing it on the server side using forms. But a unique way can become quite hectic when a user needs to input different responses for a particular form, like asking a user for ‘to do’ list. In these cases, using Django formset becomes easy both for user as well as developer, as all the input field values can be processed in a single request.

To begin using Django formsets we will create a sample form, say for entering details of a number of users .

So in our existing Django project where we want to use the django formset we will create a form in our forms.py file.

 

Class AddUserForm(forms.Form):

firstname = forms.CharField(
        widget=forms.TextInput(
            attrs={'placeholder': 'FirstName',
                   'class': "input-xlarge focused",
                   'id': "firstname",
                   }
        ))
lastname = forms.CharField(
    widget=forms.TextInput(
        attrs={
            'placeholder': 'LastName',
            'class': "input-xlarge",
            'id': "lastname",
        }
        ))
email = forms.EmailField(
    widget=forms.TextInput(
        attrs={'placeholder': 'Email',
               'class': "input-xlarge",
               'id': "emailAddress",
               }
        ))

Next, to use a formset we need to create a method that checks for total number of forms and minimum number of forms required for processing of the form.

class RequiredFormSet(BaseFormSet):
    def total_form_count(self):
        """Returns the total number of forms in this FormSet."""
        if self.data or self.files:
            return self.management_form.cleaned_data[TOTAL_FORM_COUNT]
        else:
            if self.initial_form_count() > 0:
                total_forms = self.initial_form_count()
            else:
                total_forms = self.initial_form_count() + self.extra
            if total_forms > self.max_num > 0:
                total_forms = self.max_num
            return total_forms

    def __init__(self, *args, **kwargs):
        super(RequiredFormSet, self).__init__(*args, **kwargs)
        for form in self.forms:
            form.empty_permitted = False

To make this required formset work we need to import a couple of things. In forms.py import ‘TOTAL_FORM_COUNT’ and BaseFormSet.

from django.forms.formsets import TOTAL_FORM_COUNT
from django.forms.formsets import BaseFormSet

Once we create our forms, we need to render it to our html page so we can create a class for that in our views.py:

class BulkUserView(FormView):

    template_name = 'add_bulk_user.html'
    UserAddMoreFormSet = formset_factory(AddUserForm, max_num=20, formset=RequiredFormSet)

    def get(self, request, *args, **kwargs):
        context = {}
        context['users_addmore_formset'] = self.UserAddMoreFormSet()
        return self.render_to_response(context)

Our form is initialized. We will now display in our html page.


<form method="post" action="">
        {% csrf_token %}
{{ users_addmore_formset.management_form }}
    {% for form in users_addmore_formset.forms %}
    		First Name: {{form.first_name}}
    		Last Name:{{form.last_name}}
    		Email: {{form.email}}

              <a class="delete btn btn-danger" title='Delete' href="#"><i class="icon-trash "></i>Delete</a></div>

    {% endfor %}

<div><a class="text-right icon-plus" title='ADD More reviews' id="add" href="#">Add More</a></br>
    	</div>

</form>

In order to make addition and deletion of forms work, we need to include javascript

 

  $(document).ready(function() {

  function updateElementIndex(el, prefix, ndx) {
    var id_regex = new RegExp('(' + prefix + '-\\d+-)');
    var replacement = prefix + '-' + ndx + '-';
    if ($(el).attr("for")) $(el).attr("for", $(el).attr("for").replace(id_regex,
 replacement));
    if (el.id) el.id = el.id.replace(id_regex, replacement);
    if (el.name) el.name = el.name.replace(id_regex, replacement);
  }
function deleteForm(btn, prefix) {
    var formCount = parseInt($('#id_' + prefix + '-TOTAL_FORMS').val());

    if (formCount > 1) {
      // Delete the item/form
      $(btn).parents('.item').remove();

      var forms = $('.item'); // Get all the forms

      // Update the total number of forms (1 less than before)
      $('#id_' + prefix + '-TOTAL_FORMS').val(forms.length);

      var i = 0;
      // Go through the forms and set their indices, names and IDs
      for (formCount = forms.length; i < formCount; i++) {
        $(forms.get(i)).children().children().each(function() {
          updateElementIndex(this, prefix, i);
        });
      }

    } // End if
    else {
        alert("You have to enter at least one review!");
    }
    return false;
  }

  function addForm(btn, prefix) {
    var formCount = parseInt($('#id_' + prefix + '-TOTAL_FORMS').val());

    // You can only submit a maximum of 20
    if (formCount < 20) {
      var row = $(".item:first").clone();
        $(row).find("input").val("").end();

        console.log(row)
      // Insert it after the last form
      $(row).removeAttr('id').hide().insertAfter(".item:last").slideDown(300);

      // Remove the bits we don't want in the new row/form
      // e.g. error messages
      $(".errorlist", row).remove();
      $(row).children().removeClass('error');

      // Relabel/rename all the relevant bits
      $(row).children().children().each(function() {
        updateElementIndex(this, prefix, formCount);
        if ( $(this).attr('type') == 'text' || 'select' )
          $(this).val('');
      });

      // Add an event handler for the delete item/form link
      $(row).find('.delete').click(function() {
        return deleteForm(this, prefix);
      });

      // Update the total form count
      $('#id_' + prefix + '-TOTAL_FORMS').val(formCount + 1);

    } // End if

    return false;
  }

  // Register the click event handlers
  $("#add").click(function() {
    return addForm(this, 'form');
  });

  $(".delete").click(function() {
    return deleteForm(this, 'form');
  });

});

Once we complete this process, we can save data at the server side by redirecting it to a required url. In this way. we can save lot of time in cases where users need to enter bulk data for same form over and over again. Similarly, we can process data received via multiple forms over a number of requests in a single request.

 

Leave a Reply

Your email address will not be published. Required fields are marked *

Tools & Practices

Tools and Technologies we use at Applied

Contact us now

Popular Posts