Handling Multiple Instances of Django Forms in Templates

Published Jun 02, 2017
Handling Multiple Instances of Django Forms in Templates

When you’re using Django Forms in templates, you’ll most likely run into situations where you need to handle more than one instance. Moreover, you’d need to know how to track the number of instances the user has added. This post is written to address this issue. All the code examples are based on Django 1.10, Python3, and jQuery.

To get familiar with what I am about to discuss, suppose you have a form with the following structure:

Screenshot from 2017-05-27 11-23-48.png

As you can see from the partial form, there is an option to add more contact details by clicking the add contact link, or remove the form by clicking the remove contact link. See the expanded form below:

Screenshot from 2017-05-27 11-27-14.png

I hope these two diagrams make it clear as to what we are going to achieve.

Let's get started!!

First, let’s create a model in the models.py file:

from django.db import models
class Contact(models.Model):
    CONTACT_GROUP=(("PRE-SALES","PRE-SALES"),("SALES","SALES"),("SERVICE","SERVICE"))

    contact_name = models.CharField(max_length=50,blank=True,null=True)
    contact_group = models.CharField(max_length=50,choices=CONTACT_GROUP,blank=True,null=True)
    phone = models.CharField(max_length=20,blank=True,null=True)
    email = models.EmailField(max_length=30,blank=True,null=True)
    def __str__(self):
        return self.name

Next, let's create the corresponding model form in the forms.py file:

from django import forms
from myapp.models import Contact

class ContactForm(forms.ModelForm):

    class Meta:
        model=Contact
        fields=['contact_name','contact_group','phone','email',]
        widgets = {
            'contact_name': forms.TextInput(attrs={'required': True}),
            'phone': forms.TextInput(attrs={'required': True}),
            'Email': forms.EmailInput(attrs={'required': True}),

        }

In the meta class, the model is specified using the model attribute fields. There will be a list of fields to display, and widgets can be used to add the extra attributes.

Now, to display the form on the template, we are going to use the concept of formset from Django. As per the Django documentation, a formset can be defined as:

A formset is a layer of abstraction to work with multiple forms on the same page.

To use the formset we have to import from django.forms import formset_factory.
With that clear, let's create the view, for which i will be using class-based views. The views.py will look like:

from django.forms import formset_factory
from myapp.forms import ContactForm
from django.views import View

class ContactFormView(View):
    #We are creating a formset out of the ContactForm
    Contact_FormSet=formset_factory(ContactForm)
    #The Template name where we are going to display it
    template_name="register/contacts.html"

    #Overiding the get method
    def get(self,request,*args,**kwargs):
        #Creating an Instance of formset and putting it in context dict
        context={
                'contact_form':self.Contact_FormSet(),
                }

         return render(request,self.template_name,context)

Now, let's hop over to the template and see how we can display the form. After that we will come back to the view to complete the rest of the code.
The template will be as such:

{% extends "base.html" %}
{% load static from staticfiles %}

<script src="{% static 'js/jquery.min.js' %}"></script>
<script src="{% static 'js/jquery.formset.js' %}"></script>
<div>
    <form method="post" action="{% url 'submit' %}">
        {% csrf_token %}
          <fieldset>
          <legend>Contact Details</legend>

          <div>
            {{ contact_form.management_form }}...........1
            {% for contact in contact_form %}............2
             <div class="link-formset">...................3
              {{ contact.as_p }}..........................4
            </div>
            {% endfor %}

          </div>


        </fieldset>

     <script>..........................5
    $('.link-formset').formset({
        addText: 'Add Contact',
        deleteText: 'Remove Contact'
    });
</script>

Now lets see the breakdown:

  1. {{ contact_form.management_form }} This is how Django will keep track of how many instances of the contact form have been created.
  2. Since contact_form is a formset, we have to display it using a loop.
  3. <div class="link-formset">:
    This div is used by the jQuery to provide the links for adding or removing the forms.
  4. {{ contact.as_p }}: This will display the form formatted with <p> tags.
  5. The script will do the magic of adding or removing the links. For that you will need jQuery and this jQuery Plugin. Add both .js files in your static directory and in the template.

Next, let’s return back to our view to see how we can handle the form submissions:

  #Overiding the post method
  def post(self,request,*args,**kwargs):
      contact_formset=self.Contact_Formset(self.request.POST)

      #Checking the if the form is valid
      if contact_formset.is_valid():
          #To save we have loop through the formset
          for contacts in contact_formset:
              #Saving in the contacts models	
              contacts.save()

          return HttpResponseRedirect(reverse("success-url"))

      else:
          context={
                  'contact_form':self.Contact_FormSet(),
                  }

           return render(request,self.template_name,context)

In the app/urls.py add the following :

  from django.conf.urls import url
  from myapp import views

  urlpatterns=[
      url(r'^contact/$',ContactFormView.as_view(),name='submit'),
      ]

Finally, you can see how easy it is to handle multiple instances of forms in Django.

Happy Django Coding!!! 😃

Discover and read more posts from Ankur Rathore
get started
Enjoy this post?

Leave a like and comment for Ankur

Get curated posts in your inbox

Read more posts to become a better developer