12

How to add a dynamic form to a django formset in templates without annoying copies of html template output?

I have a formset with unknown count of result forms, and I need to add a few forms directly in template by pressing on a button.

1 Answer 1

42

This self-answer is based on this post by Nick Lang, but we are going to simplify this way and we don't need to copy/paste whole form html anymore.

We have an inline formset which is created in a view like this:

items_formset = inlineformset_factory(Parent, Item, form=ItemForm, extra=1)
item_forms = items_formset()  

Next, we need to create a template for out formset form, we can do it using empty_form property of formset instance, which generates a html form template where every "id" number of a form is replaced by __prefix__ string, for example:

<!--
{{ item_forms.empty_form }}

{# <!-- or for crispy forms --> {% crispy item_forms.empty_form item_forms.form.helper %} #}
-->  

So, first we need to replace this __prefix__ by an id and add a form using this template.
Here is a form template code fragment, which we can use to create new elements:

<script type="text/html" id="item-template">
<div id="item-__prefix__">
    {{ item_forms.empty_form }}
    <!-- crispy: {% crispy item_forms.empty_form item_forms.form.helper %} -->
</div>
</script>

Then we need to display main part of the form:

<form action="" method="post">
{% csrf_token %}
{{ item_forms.management_form }}
<div id="items-form-container">
    {% for item_form in item_forms %}
        <div id="item-{{ forloop.counter0 }}">
            {{ item_form.id }}
            {{ item_form.as_p }}
            {# <!-- or for crispy forms --> {% crispy item_form %} #}
        </div>
    {% endfor %}
</div>
<a href="#" id="add-item-button" class="btn btn-info add-item">Add Item</a>
</form>

Finally we need to add some JS (jquery, tested with 1.9.1 and 2.1.0) to add a next formset form. Note we will not use underscore.js, since it's unneeded in this case: just str.replace to replace __prefix__ by next "id" number)

<script>
$(document).ready(function() {
    $('.add-item').click(function(ev) {
        ev.preventDefault();
        var count = $('#items-form-container').children().length;
        var tmplMarkup = $('#item-template').html();
        var compiledTmpl = tmplMarkup.replace(/__prefix__/g, count);
        $('div#items-form-container').append(compiledTmpl);

        // update form count
        $('#id_item_items-TOTAL_FORMS').attr('value', count+1);

        // some animate to scroll to view our new form
        $('html, body').animate({
                scrollTop: $("#add-item-button").position().top-200
            }, 800);
    });
});
</script>

That's all, just click on "Add Item" button, and a new formset item will appear.

Be sure to replace this sample by your app_name/model_name.

Sign up to request clarification or add additional context in comments.

2 Comments

What if I have an existing form with two fields and I need to add to it extra fields?
user1919: In this case you will have to recreate a form structure by post data in python, you can add fields to "self.fields" dictionary in form __init__() , at the client side you have to create fields with different names. I used knockout.js to create fields schema, you can check this example knockoutjs.com/examples/contactsEditor.html

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.