Dynamically adding forms to a Django FormSet with an add button using jQuery
Overview
FormSet’s are a powerful Django CMS feature that makes it easy to display an arbitrary number of forms of the same form class and then process them all together.
There is a missing functionality that you would naturally expect them to have, that is to have a button where you can let your users add the amount of forms they desire when displaying the template.
There is a very popular approach of cloning a div based in this StackOverflow’s answer, where it uses Javascript to manually clone a previous form present in the webpage.
There is an easier approach that I will be covering here.
Understanding
Form Skel
Django’s Formsets come with a special attribute:
empty-form,
which returns a form instance with a prefix of
__prefix__
.
This way we have an empty form ready to be used when displaying
formsets where just need to replace the __prefix__
part with the
current form position to define its order among all the other forms
with Javascript.
FormSet special attrbs
When adding and removing a form dynamically we should also set the following special fields of management formsets:
# special field names
TOTAL_FORM_COUNT = 'TOTAL_FORMS'
INITIAL_FORM_COUNT = 'INITIAL_FORMS'
MIN_NUM_FORM_COUNT = 'MIN_NUM_FORMS'
MAX_NUM_FORM_COUNT = 'MAX_NUM_FORMS'
ORDERING_FIELD_NAME = 'ORDER'
DELETION_FIELD_NAME = 'DELETE'
# default minimum number of forms in a formset
DEFAULT_MIN_NUM = 0
# default maximum number of forms in a formset, to prevent memory exhaustion
DEFAULT_MAX_NUM = 1000
These fields are used in FormSet validation and control how many form instances are being displayed.
So they should also have to change in synchronization with our new added forms
These variables are used when using {{ my_formset.management_form }}
to render our form.
When we send POST data, we should include these variables to avoid
receiving a django.forms.utils.ValidationError: ['ManagementForm data is missing or has been tampered with']
validation error, for example,
for a form myform
where we added just one form, POST data would
look like:
data = {
# each form field data with a proper index form
'myformset-0-raw': 'my raw field string',
# form status, number of forms
'myformset-INITIAL_FORMS': 1,
'myformset-TOTAL_FORMS': 2,
}
Adding to a Formset
This is the actual code we need to insert in the template:
<h3>FormSet example</h3>
{{ myformset.management_form }}
<div id="form_set">
{% for form in myformset.forms %}
{{form.non_field_errors}}
{{form.errors}}
<table class='no_error'>
{{ form }}
</table>
{% endfor %}
</div>
<input type="button" value="Add More" id="add_more">
<div id="empty_form" style="display:none">
<table class='no_error'>
{{ myformset.empty_form }}
</table>
</div>
Here we are including the number of forms we have in
{{myformset.management_form}}
, then looping over all of the forms we
have in the formset myformset.forms
, display the button to add more
forms, and finally have the skeleton for new forms hidden.
Then adding the button with the following Javscript code:
$('#add_more').click(function() {
var form_idx = $('#id_form-TOTAL_FORMS').val();
$('#form_set').append($('#empty_form').html().replace(/__prefix__/g, form_idx));
$('#id_form-TOTAL_FORMS').val(parseInt(form_idx) + 1);
});
Each time we press the Add button, it will copy the skeleton form, replace
the __prefix__
placeholder with the right form position and update the
total number of forms so Django validation knows how many forms we are
sending.
Possible Errors
ReferenceError: $ is not defined
If you get ReferenceError: $ is not defined
in your browser’s
console, then it is due having defined the script before loading
JQuery, just load the javascript code after inserting the JQuery
library.
References
- Understanding a Management Form https://docs.djangoproject.com/en/2.1/topics/forms/formsets/#understanding-the-managementform
- https://docs.djangoproject.com/en/dev/topics/forms/formsets/#empty-form
- Main code from Dynamically adding a form to a Django formset with Ajax by Dave
- ReferenceError: $ is not defined
- August 1, 2023
- How to create a reusable Django app and distribute it with PIP or publish to pypi.orgJune 29, 2021
- How To Serve Multiple Django Applications with uWSGI and Nginx in Ubuntu 20.04October 26, 2020
- How to add favicon to Django in 4 stepsSeptember 3, 2020
- Categories in Django with BreadcrumbsAugust 30, 2020
- How To Migrate From SQLite To PostgreSQL In Django In 3 stepsAugust 28, 2020
- Practical guide to internationalize a Django app in 5 steps.August 24, 2020
- Disable new users singup when using Django's allauth packageSeptember 3, 2019
- How to add ads.txt to Django as requested by Google AdsenseAugust 30, 2019
- Have multiple submit buttons for the same Django formJuly 2, 2019
- Better Testing with Page Object Design in DjangoMay 1, 2019
- Generating slugs automatically in Django without packages - Two easy and solid approachesFebruary 14, 2019
- How to set up Django tests to use a free PostgreSQL database in HerokuFebruary 13, 2019
- Dynamically adding forms to a Django FormSet with an add button using jQuery
- Use of Django's static templatetag in css file to set a background imageFebruary 1, 2019
- Activate Django's manage.py commands completion in Bash in 2 stepsJanuary 29, 2019
- Sending Emails with Django using SendGrid in 3 easy stepsJanuary 9, 2019
- Adding Users to Your Django Project With A Custom User ModelSeptember 21, 2018
- Setting Up A Factory For One To Many Relationships In FactoryboyApril 17, 2018
- Generate UML class diagrams from django modelsMarch 24, 2018
- Set Up Ubuntu To Serve A Django Website Step By StepJuly 3, 2017
- Django Project Directory StructureJuly 16, 2016
- How to Have Different Django Settings for Development and Production, and environment isolationJune 10, 2016
- Django OverviewJune 2, 2016
Django Forms
- Adding a Cancel button in Django class-based views, editing views and formsJuly 15, 2019
- Using Django Model Primary Key in Custom Forms THE RIGHT WAYJuly 13, 2019
- Django formset handling with class based views, custom errors and validationJuly 4, 2019
- How To Use Bootstrap 4 In Django FormsMay 25, 2018
- Understanding Django FormsApril 30, 2018
- How To Create A Form In DjangoJuly 29, 2016
Articles
Subcategories
Except as otherwise noted, the content of this page is licensed under CC BY-NC-ND 4.0 . Terms and Policy.
Powered by SimpleIT Hugo Theme
·