over 1 year ago

Django's formset can be difficult to understand, so I will try to explain it in a more understanbable way here.

What is it used for?

Let's say you are building a website which keeps track of users' daily activities. They can logon to your site and fill out a form with the following information:

  • Date and time
  • Activity name
  • Duration

Multiple activities can be recorded throughout a day. When users start to record for a new day, he should see an empty form with the aforementioned fields and an add button below the form to add more activities:

Date and Time: ___________________
Activity Name: ___________________
Duration:      ___________________

[ + Add More Activity ]

In this case, you cannot predict how many activities users will add beforehand. Thus, you cannot create a form to prepare for it. This is when Formset comes in. Formset allows you to add/remove forms on the fly (with the help of javascript which I will talk about in detail later).

How it works?

Before getting into the implementation details, it is important to understand how formset works. The best way to learn it is to think out how you would implement it by hand.


CRUD is a good starting point to define what needs to be accomplished:

  • Forms need to be created (i.e. create new form data that do not exist in the database)
  • Forms need to be read (i.e. fill forms with existing data and display them to the user)
  • Forms need to be updated (i.e. fetch existing data and save changes made to them)
  • Forms need to be deleted (i.e. delete an existing form)

To accomplish any of these tasks, we need a way to keep track of forms. Thus, an ID is needed for each form. Also, when a form is deleted, we need a way to detect that. Thus, a form needs to be marked as deleted or not deleted.

How does Django do it?

To manage which form is which and mark them as deleted when neccessary, Django insert hidden input fields into the rendered HTML to help itself keeping track of the forms. Django inserts the following hidden data:

<input id="id_form_set-TOTAL_FORMS" name="form_set-TOTAL_FORMS" type="hidden" value="1"> 

TOTAL_FORMS:  the total number of forms in this formset
value: 1 means this formset contains 1 form 
<input id="id_form_set-INITIAL_FORMS" name="form_set-INITIAL_FORMS" type="hidden" value="1">

INITIAL_FORMS:  the number of forms in the formset that were pre-filled
value: 1 means 1 form within this formset has been pre-filled with data
<input id="id_form_set-MIN_NUM_FORMS" name="form_set-MIN_NUM_FORMS" type="hidden" value="0">

MIN_NUM_FORMS:  the minimum number of forms within the formset
value: the minimum number, but you normally do not need to set this
<input id="id_form_set-MAX_NUM_FORMS" name="form_set-MAX_NUM_FORMS" type="hidden" value="1000">

MAX_NUM_FORMS:  the maximum number of forms a formset may contain
value: the maximum number allowed

All of these hidden data, are added by the ManagementForm. You don't need to read about ManagementForm at the moment, just knowing which componment within Django is responsible for these is enough at this point.

To keep track of each form, Django also inserts hidden primary keys that are associated with existing forms:

<input id="id_form_set-0-id" name="form_set-0-id" type="hidden" value="4">

value 4 means the primary key of this form is 4

To keep track of which form has been deleted, Django also inserts a hidden field:

<input type="hidden" name="form_set-0-DELETE" id="id_form_set-0-DELETE" value="on"> 
value: on means this form is marked as deleted. When there is no value, it means the form is not deleted

The can_delete parameter is responsible for marking forms as deleted. I will explain this parameter later.

This post is long enough for a short read. I will explain more details about formset in the next blog post.

← Django how to reverse urls belonged to other apps Django OneToOneField and Iterating Model's Fields →
comments powered by Disqus