0

I am trying to save formset extra fields data using forms and views. Eg:-Team has no.of players. so i want to add new player by click on add more button.The code i tried below. the problem is if i add more than one player at a time...it is saving last object value only o/p for below code

models.py

from django.db import models

class Player(models.Model):
    pname = models.CharField(max_length=50)
    hscore = models.IntegerField()
    age = models.IntegerField()
    def __str__(self):
       return self.pname

class Team(models.Model):
    tname = models.CharField(max_length=100)
    player= models.ManyToManyField(Player)
    def __str__(self):
        return self.tname

forms.py

from django import forms
from django.forms.formsets import formset_factory

class PlayerForm(forms.Form):
    pname = forms.CharField()
    hscore= forms.IntegerField()
    age = forms.IntegerField()

PlayerFormset= formset_factory(PlayerForm)

class TeamForm(forms.Form):
   tname= forms.CharField()
   player= PlayerFormset()

views.py

from django.shortcuts import render, get_object_or_404,redirect
from .models import Player,Team
from .forms import TeamForm,PlayerFormset
from django.contrib import messages
from django.contrib.auth.decorators import login_required
from django import forms
from django.forms import formset_factory

def post(request):

   if request.POST:
        form = TeamForm(request.POST)
        form.player_instances = PlayerFormset(request.POST)
        if form.is_valid():
        team= Team()
        team.tname= form.cleaned_data['tname']
        team.save()

        if form.player_instances.cleaned_data is not None:

            for item in form.player_instances.cleaned_data:
                player = Player()
                player.pname= item['pname']
                player.hscore= item['hscore']
                player.age= item['age']
                player.save()
                team.player.add(player)
            team.save()

   else:
        form = TeamForm()
        return render(request, 'new.html', {'form':form})

template: new.html

<html>
<head>

    <title>gffdfdf</title>
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css">
    <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.2.1/jquery.min.js"></script>
    <script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js"></script>

 </head>
<body>

 <div class="container">

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

  <h3> Product Instance(s)</h3>
  <table id="table-product" class="table">
    <thead>
       <tr>
          <th>player name</th>
         <th>highest score</th>   
         <th>age</th>
       </tr>

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

    <tr> 
        <td>{{ player.pname  }}</td>
        <td>{{ player.hscore }}</td>   
         <td>{{ player.age }}</td>
         <td> <input id="input_add" type="button" name="add" value=" Add More " class="tr_clone_add btn data_input"> </td>
      </tr>

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

   </form>
   </div>
   <script>
       var i = 1;
    $("#input_add").click(function() {
        $("tbody tr:first").clone().find(".data_input").each(function() {
            if ($(this).attr('class')== 'tr_clone_add btn data_input'){
                $(this).attr({
                    'id': function(_, id) { return "remove_button" },
                    'name': function(_, name) { return "name_remove" +i },
                    'value': 'Remove'
                }).on("click", function(){
                    var a = $(this).parent();
                    var b= a.parent();
                    i=i-1
                    $('#id_form-TOTAL_FORMS').val(i);
                    b.remove();

                    $('.player-instances tr').each(function(index, value){
                        $(this).find('.data_input').each(function(){
                            $(this).attr({
                                'id': function (_, id) {
                                     var idData= id;
                                    var splitV= String(idData).split('-');
                                    var fData= splitV[0];
                                    var tData= splitV[2];
                                    return fData+ "-" +index + "-" + tData
                                },
                                'name': function (_, name) {
                                    var nameData= name;
                                    var splitV= String(nameData).split('-');
                                    var fData= splitV[0];
                                    var tData= splitV[2];
                                    return fData+ "-" +index + "-" + tData
                                }
                            });
                        })
                    })
                })
            }
            else{
                $(this).attr({
                    'id': function (_, id) {
                        var idData= id;
                        var splitV= String(idData).split('-');
                        var fData= splitV[0];
                        var tData= splitV[2];
                        return fData+ "-" +i + "-" + tData
                    },
                    'name': function (_, name) {
                        var nameData= name;
                        var splitV= String(nameData).split('-');
                        var fData= splitV[0];
                        var tData= splitV[2];
                        return fData+ "-" +i + "-" + tData
                    }
                });

            }
        }).end().appendTo("tbody");
        $('#id_form-TOTAL_FORMS').val(1+i);
        i++;

    });
</script>
</body>
</html>

===============================================================

I am unable to save all objects. please correct it

3 Answers3

0

You need to loop over form.player_instances.

if request.POST:
    form = TeamForm(request.POST)
    form.player_instances = PlayerFormset(request.POST)
    if form.is_valid():
    team= Team()
    team.tname= form.cleaned_data['tname']
    team.save()

    if form.player_instances.is_valid():

        for item in form.player_instances:
            player = Player()
            player.pname= item.cleaned_data['pname']
            player.hscore= item.cleaned_data['hscore']
            player.age= item.cleaned_data['age']
            player.save()
            team.player.add(player)
            team.save()
Ankita Gupta
  • 573
  • 3
  • 13
0

The reason why you only can save first one of items is your added line id and name still is id_form-0-pname not change to id_form-1-pname(pattern is id_form-i-pname).

Another way to achieve this,use lib django-dynamic-formset,code is here:

news.html

<html>
<head>

    <title>gffdfdf</title>
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css">

    <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.2.1/jquery.min.js"></script>
    <script src="/static/jquery.formset.js"></script>
    <script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js"></script>

</head>
<body>

<div class="container">

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

        <h3> Product Instance(s)</h3>
        <table id="table-product" class="table">
            <thead>
            <tr>
                <th>player name</th>
                <th>highest score</th>
                <th>age</th>
            </tr>

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

                <tr>
                    <td>{{ player.pname }}</td>
                    <td>{{ player.hscore }}</td>
                    <td>{{ player.age }}</td>
                </tr>

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

    </form>
</div>
<script>
    $(function () {
        $('#myForm tbody tr').formset();
    })
</script>
</body>
</html>

It's look like enter image description here it's simple and work. jquery.formset.js can be downloaded from github in here.Doc is here. You can change the text and css of add\remove link with conf jquery.formset.js like:

/* Setup plugin defaults */
$.fn.formset.defaults = {
    prefix: 'form',                  // The form prefix for your django formset
    formTemplate: null,              // The jQuery selection cloned to generate new form instances
    addText: 'add another',          // Text for the add link
    deleteText: 'remove',            // Text for the delete link
    addCssClass: 'add-row',          // CSS class applied to the add link
    deleteCssClass: 'delete-row',    // CSS class applied to the delete link
    formCssClass: 'dynamic-form',    // CSS class applied to each form in a formset
    extraClasses: [],                // Additional CSS classes, which will be applied to each form in turn
    keepFieldValues: '',             // jQuery selector for fields whose values should be kept when the form is cloned
    added: null,                     // Function called each time a new form is added
    removed: null                    // Function called each time a form is deleted
};
Ykh
  • 7,567
  • 1
  • 22
  • 31
  • Thank you.. i tried above code... but this time "ValidationError at /post-new/ ['ManagementForm data is missing or has been tampered with']" – Raja Polipilli Dec 11 '17 at 11:51
  • It's `form.player.management_form` not `form.players.management_form`,there is no s behind player – Ykh Dec 12 '17 at 01:08
  • https://stackoverflow.com/questions/62299126/django-formset?noredirect=1#comment110185775_62299126 can you please help me – art_cs Jun 10 '20 at 11:36
0

**Use this following for your new.html file and keep your other files unchanged like forms and models **

<html>
<head>

    <title>gffdfdf</title>
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css">
    <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.2.1/jquery.min.js"></script>
    <script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery.formset/1.2.2/jquery.formset.js"></script>

</head>
<body>

<div class="container">

    <form method="post" class="">
        {% csrf_token %}
        <h2> Team</h2>
        {% for field in form %}
            {{ field.errors }}
            {{ field.label_tag }} : {{ field }}
        {% endfor %}
        {{ form.players.management_form }}
       <div style="display: none" > {{ form.players.empty_form }}</div>

        <h3> Product Instance(s)</h3>
        <table id="table-product" class="table">
            <thead>
            <tr>
                <th>player name</th>
                <th>highest score</th>
                <th>age</th>
            </tr>

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

                <tr>
                    <td>{{ player.pname  }}</td>
                    <td>{{ player.hscore }}</td>
                    <td>{{ player.age }}</td>
                    <td> <input id="input_add" type="button" name="add" value=" Add More " class="tr_clone_add btn data_input"> </td>
                </tr>


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

    </form>
</div>
<script>

    $("#input_add").click(function() {
        let formCount = parseInt($('#id_form-TOTAL_FORMS').val());
        var html = `<tr>
                        <td>{{ form.players.empty_form.pname }}</td>
                        <td>{{ form.players.empty_form.hscore }}</td>
                        <td>{{ form.players.empty_form.age }}</td>
                        <td> <input id="input_add" type="button" name="add" value=" Add More " class="tr_clone_add btn data_input"> </td>
                     </tr>`;
       html =  html.replace(/__prefix__/g, formCount);
        $('#id_form-TOTAL_FORMS').val(formCount + 1);
        $('#playerFrmTableId').append(html);
    });
</script>
</body>
</html>