Duplicate a Django modelformset_factory Form

I created a formset_factory and wanted to have a simple “click me to add another form”. This seemed like a routine task, but the solutions I found online were unnecessarily complicated or required me to install a separate Django app, which I had no intention of doing.

So I created my own…

The only pre-requirement that this needs besides standard Django is jQuery.

So here is a rough overview of how this works:

  • Create a modelformset in my views.py and send it to the template.
  • Add in a link that’s executed to trigger the new form adding.
  • Django’s formset_factory’s required management_form creates the id_form-TOTAL_FORMS hidden variable. The jQuery must update this value.
  • Have jQuery locate the current form and create a blank copy from.
  • Update the name and id parameters of the copied form using jQuery
  • Identify where to paste the new form
  • Paste it there!

Here is my views.py snippet. In my case, the model is called “Component” and the related form is called “ComponentForm”. I define them as follows:

ComponentFormSet = modelformset_factory(Component, form=ComponentForm)
componentformset = ComponentFormSet( queryset=Component.objects.none() )

The queryset must be set to Component.objects.none() for whatever reason, otherwise you will get the latest Component value. I pass the “componentformset” in the context to the template.

Next, the HTML must be rendered in the template as follows. Noticed that I used componentformset.0 with the “.0“. Why? Because componentformset, being a modelformset_factory, is a set of forms, not an individual form. We have an individual form, so I am only going to display the first one in the list. After that, the template includes a tbody called “formtemplate”. This will later tell our jQuery where to copy our template from. Finally, the <a> tag is necessary to tell jQuery when to add a new form. The newcomponents <tbody> is where the new form will be pasted.

{{ componentformset.management_form }}
<tbody id="formtemplate">
{{ componentformset.0 }}

<tbody id="newcomponents">
<a href="#" id='addForm'>Add Component</a>

Next is the Javascript. (In my actual production code I put this above the HTML, but you could really put this anywhere since it’ll only trigger when the page fully renders).

<script type="text/javascript">
    $(document).ready(function() {
      var addForm = $('#addForm');
      var formNumberObject = $('#id_form-TOTAL_FORMS');

      var addForm = function(e) {
        newFormCount = parseInt(formNumberObject.val()) + 1;
        formNumberObject.val( newFormCount );

        var tabletemplate = $('#formtemplate tr').clone();
        var pastehere = $('#newcomponents');

        changevalues = tabletemplate.find('[id^=id_form-0]');
        changevalues.each( function(i, index) {
          currentid = $(this).attr('id').replace(/(id_form-)[0-9]+/, 'id_form-' + (newFormCount-1) );
          $(this).attr('id', currentid);

          currentname = $(this).attr('name').replace(/(form-)[0-9]+/, 'form-' + (newFormCount-1) );
          $(this).attr('name', currentname);


      addForm.on('click', addForm);

The JS does the following. This is in order of the logic of the program, not in order of the code above, but you should be able to piece it together.

  • Identify the ‘addForm’ <a> link and set it to execute the function ‘addForm’ (yes, they have the same name, but you can change that)
  • Identify the id_form-TOTAL_FORMS by ID in the rendered HTML. Again, this is produced by the management_form part of our formset_factory.
  • Identify the number of forms. I suppose I could have just set this to 0 by default, but I’ll have the JS do it for me.
  • When the user clicks the ‘addForm’ link, it will execute the function ‘addForm’, which does the following:
    • Add 1 to the current newFormCount value.
    • Create a copy of the form and store it in “tabletemplate”.
    • Identify where to paste the data, identified by “newcomponents”.
    • Find each instance where the ID is in the RegEx pattern “id_form-0”
    • Iterate for each instance, and replace the number 0 with the current number. This will keep the individual component name intact, but update the count.
  • Append the newly created form to the “pastehere” tbody.

With that, you should be able to add a new form with the click of a button!

Thoughts? Comments? Please do let me know so I can fix any mistakes or update the code as needed. And remember…Free Palestine!


About Nahraf
Providing interesting insight into the world of Economics, Theology, Computer Science and Social phenomena.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: