How add row dynamically in django formset

In my django app I have two models i.e Product and Real which are connected by many to many relationship.

To add the data dynamically in my tables I want to use javascript to Add row or Remove Row button in my forms but unable to do so.

Here are the details:

Models.py

from dataclasses import Field
from django.db import models

class Product(models.Model):
    name = models.CharField(max_length = 150)
    quantity = models.IntegerField()
    cost = models.DecimalField(max_digits=15, decimal_places=0)
    sell = models.DecimalField(max_digits=15, decimal_places=0)

class Real(models.Model):
    payment_id = models.CharField(max_length = 150)
    company = models.CharField(max_length = 150)
    product = models.ManyToManyField(Product, symmetrical=False)

Forms.py

from .models import Product, Real
from django import forms
from django.forms import formset_factory

class ProductForm(forms.Form):
    name = forms.CharField()
    cost = forms.DecimalField()
    sell = forms.DecimalField()
    quantity = forms.IntegerField()

ProductFormset = formset_factory(ProductForm)

class RealForm(forms.Form):
   payment_id = forms.CharField()
   company = forms.CharField()
   product = ProductFormset()

Views.py

from django.shortcuts import render, redirect
from django.forms import modelformset_factory
from .models import Product, Real
from .forms import RealForm, ProductForm, ProductFormset

def post(request):

   if request.POST:
        form = Real(request.POST)
        form.product_instances = ProductFormset(request.POST)
        if form.is_valid():
            real = Real()
            real.payment_id = form.cleaned_data['payment_id']
            real.save()

        if form.product_instances.cleaned_data is not None:

            for item in form.product_instances.cleaned_data:
                product = Product()
                product.name = item['name']
                product.cost = item['cost']
                product.sell = item['sell']
                product.sell = item['quantity']
                product.save()
                real.product.add(product)
            real.save()

   else:
        form = RealForm()
        return render(request, 'test.html', {'form':form})

def my_formset_view(request):
    response = None
    formset_class = modelformset_factory(model=Product, fields=('name', 'cost', 'sell'), extra=0, can_delete=True)
    if request.method == 'POST':
        formset = formset_class(data=request.POST)
            
        if formset.is_valid():
            formset.save()
            response = redirect(to='my_success_view')
    else:
        formset = formset_class()
        
    if response is None:
        response = render(
            request, 'test.html', dict(formset=formset))
        
    return response

test.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>
    <div class="container">

        <form id="myForm" action="" method="post" class="">
            {% csrf_token %}
            <h2>Real</h2>
            {% for field in form %}
                {{ field.errors }}
                {{ field.label_tag }} : {{ field }}
            {% endfor %}
            {{ form.product.management_form }}

            <h3>REAL Instance(s)</h3>
            <table id="table-product" class="table">
                <thead>
                <tr>
                    <th>name</th>
                    <th>cost</th>
                    <th>sell</th>
                </tr>

                </thead>
                {% for real in form.real %}
                    <tbody class="player-instances">

                    <tr>
                        <td>{{ product.name }}</td>
                        <td>{{ product.cost }}</td>
                        <td>{{ product.sell }}</td>
                    </tr>

                    </tbody>
                {% endfor %}
            </table>
            <button type="submit" class="btn btn-primary">save</button>

        </form>
    </div>

    <template id="id_formset_empty_form">{{ formset.empty_form }}</template>
    <form method="post" id="id_html_form" autocomplete="off">
        {% csrf_token %}
        <table id="id_formset_container">
            {{ formset }}
        </table>
        <div id="id_formset_add_button" style="text-decoration: underline; cursor: pointer;">Add</div>
        <input id="id_formset_submit_button" type="submit" value="Submit">
    </form>

    <script type="application/javascript">
        $(function () {
            $('#myForm tbody tr').formset();
        })
        window.addEventListener('load', (event) => {
            // get form template and total number of forms from management form
            const templateForm = document.getElementById('id_formset_empty_form');
            const inputTotalForms = document.querySelector('input[id$="-TOTAL_FORMS"]');
            const inputInitialForms = document.querySelector('input[id$="-INITIAL_FORMS"]');
        
            // get our container (e.g. <table>, <ul>, or <div>) and "Add" button
            const containerFormSet = document.getElementById('id_formset_container');
            const buttonAdd = document.getElementById('id_formset_add_button');
            const buttonSubmit = document.getElementById('id_formset_submit_button');
        
            // event handlers
            buttonAdd.onclick = addForm;
            buttonSubmit.onclick = updateNameAttributes;
        
            // form counters (note: proper form index bookkeeping is necessary
            // because django's formset will create empty forms for any missing
            // indices, and will discard forms with indices >= TOTAL_FORMS, which can
            // lead to funny behavior in some edge cases)
            const initialForms = Number(inputInitialForms.value);
            let extraFormIndices = [];
            let nextFormIndex = initialForms;
        
            function addForm () {
                // create DocumentFragment from template
                const formFragment = templateForm.content.cloneNode(true);
                // a django form is rendered as_table (default), as_ul, or as_p, so
                // the fragment will contain one or more <tr>, <li>, or <p> elements,
                // respectively.
                for (let element of formFragment.children) {
                    // replace the __prefix__ placeholders from the empty form by the
                    // actual form index
                    element.innerHTML = element.innerHTML.replace('/(?<=\w+-)(__prefix__|\d+)(?=-\w+)/g', nextFormIndex.toString());
                    // add a custom attribute to simplify bookkeeping
                    element.dataset.formIndex = nextFormIndex.toString();
                    // add a delete click handler (if formset can_delete)
                    setDeleteHandler(element);
                }
                // move the fragment's children onto the DOM
                // (the fragment is empty afterwards)
                containerFormSet.appendChild(formFragment);
                // keep track of form indices
                extraFormIndices.push(nextFormIndex++);
            }
        
            function removeForm (event) {
                // remove all elements with form-index matching that of the delete-input
                const formIndex = event.target.dataset.formIndex;
                for (let element of getFormElements(formIndex)) {
                    element.remove();
                }
                // remove form index from array
                let indexIndex = extraFormIndices.indexOf(Number(formIndex));
                if (indexIndex > -1) {
                    extraFormIndices.splice(indexIndex, 1);
                }
            }
        
            function setDeleteHandler (containerElement) {
                // modify DELETE checkbox in containerElement, if the checkbox exists
                // (these checboxes are added by formset if can_delete)
                const inputDelete = containerElement.querySelector('input[id$="-DELETE"]');
                if (inputDelete) {
                    // duplicate the form index instead of relying on parentElement (more robust)
                    inputDelete.dataset.formIndex = containerElement.dataset.formIndex;
                    inputDelete.onclick = removeForm;
                }
            }
        
            function getFormElements(index) {
                // the data-form-index attribute is available as dataset.formIndex
                // https://developer.mozilla.org/en-US/docs/Learn/HTML/Howto/Use_data_attributes#javascript_access
                return containerFormSet.querySelectorAll('[data-form-index="' + index + '"]');
            }
        
            function updateNameAttributes (event) {
                // make sure the name indices are consecutive and smaller than
                // TOTAL_FORMS (the name attributes end up as dict keys on the server)
                // note we do not need to update the indices in the id attributes etc.
                for (let [consecutiveIndex, formIndex] of extraFormIndices.entries()) {
                    for (let formElement of getFormElements(formIndex)){
                        for (let element of formElement.querySelectorAll('input, select')) {
                            if ('name' in element) {
                                element.name = element.name.replace(
                                    '/(?<=\w+-)(__prefix__|\d+)(?=-\w+)/g',
                                    (initialForms + consecutiveIndex).toString());
                            }
                        }
                    }
                }
                updateTotalFormCount();
            }
        
            function updateTotalFormCount (event) {
                // note we could simply do initialForms + extraFormIndices.length
                // to get the total form count, but that does not work if we have
                // validation errors on forms that were added dynamically
                const firstElement = templateForm.content.querySelector('input, select');
                // select the first input or select element, then count how many ids
                // with the same suffix occur in the formset container
                if (firstElement) {
                    let suffix = firstElement.id.split('__prefix__')[1];
                    let selector = firstElement.tagName.toLowerCase() + '[id$="' + suffix + '"]';
                    let allElementsForId = containerFormSet.querySelectorAll(selector);
                    // update total form count
                    inputTotalForms.value = allElementsForId.length;
                }
            }
        }, false);
    </script>

</body>
</html>

but django doesn't output anything

enter image description here

Its will be like this

Back to Top