Home > Back-end >  Django formsets not saving all forms to the database
Django formsets not saving all forms to the database

Time:08-25

  1. I have a formset to render multiple forms at once. By default only two forms are rendered.
  2. Using JavaScript, I'm adding another two forms to the formset, so the total number of forms is now four.
  3. When I sent POST request, my view saves only first two objects to the database. The data from forms, added using JavaScript, is not saved.

What am I doing wrong? My code:

This is my forms.py file, where i define my formset.

class AddAnswerForm(ModelForm):
    class Meta:
        model = Answer
        exclude = ['question']
        widgets = {
            'answer_text': forms.TextInput(attrs={'class': 'input'}),
        }

AnswerFormSet = modelformset_factory(Answer, form=AddAnswerForm, extra=2, max_num=4)

This is JS code, which I'm using to add new forms to the page (it's not mine code, but it works).

<script>
  let answerForm = document.querySelectorAll(".answer-form")
  let container = document.querySelector("#question-form")
  let addButton = document.querySelector("#add-form")
  let totalForms = document.querySelector("#id_form-TOTAL_FORMS")

  let formNum = answerForm.length-1
  addButton.addEventListener('click', addForm)

  function addForm(e){
    e.preventDefault()

    let newForm = answerForm[0].cloneNode(true)
    let formRegex = RegExp(`form-(\\d){1}-`, 'g')

    formNum  
    newForm.innerHTML = newForm.innerHTML.replace(formRegex, `form=${formNum}-`)
    container.insertBefore(newForm, addButton)

    totalForms.setAttribute('value', `${formNum 1}`)
  }
</script> 

And here is my view.

class QuestionAddView(View):
    form_class = NewQuestionForm
    form_class_for_answers = AnswerFormSet
    template_name = 'questions/question_add.html'

    def get(self, request, *args, **kwargs):
        form = self.form_class()
        answers_form = self.form_class_for_answers(queryset=Answer.objects.none())

        return render(request, self.template_name, {'form': form, 'answers_form': answers_form})

    def post(self, request, *args, **kwargs):
        form = self.form_class(request.POST, request.FILES)
        answers_form = self.form_class_for_answers(request.POST)
        print(request.POST)
        if form.is_valid() and answers_form.is_valid():
            new_question = form.save(commit=False)
            new_question.author = request.user
            new_question.save()
            print(answers_form)

            instances = answers_form.save(commit=False)
            print(instances)
            for instance in instances:
                print(instance)
                instance.question = new_question
                instance.save()

            return HttpResponseRedirect('/')

print(request.POST) prints:

<QueryDict: {'csrfmiddlewaretoken': ['eTaR0N4LgG1WvfK3zshqOcEMYUMvXWkxctfS6OXD9E2rMbMe5VrlTVsBgnjoM2zd'], 'title': ['321321'], 'picture': [''], 'body': ['123123'], 'difficulty': ['None'], 'answer_explanation': ['12321'], 'form-TOTAL_FORMS': ['4'], 'form-INITIAL_FORMS': ['0'], 'form-MIN_NUM_FORMS': ['0'], 'form-MAX_NUM_FORMS': ['4'], 'form-0-answer_text': ['orage'], 'form-0-id': [''], 'form-1-answer_text': ['apple'], 'form-1-id': [''], 'form=2-answer_text': ['grapes'], 'form=2-id': [''], 'form=3-answer_text': ['pear'], 'form=3-id': ['']}>

print(answers_form) print this. So, I assume JS script in doing it's job right

<input type="hidden" name="form-TOTAL_FORMS" value="4" id="id_form-TOTAL_FORMS"><input type="hidden" name="form-INITIAL_FORMS" value="0" id="id_form-INITIAL_FORMS"><input type="hidden" name="form-MIN_NUM_FORMS" value="0" id="id_form-MIN_NUM_FORMS"><input type="hidden" name="form-MAX_NUM_FORMS" value="4" id="id_form-MAX_NUM_FORMS"><tr>
    <th><label for="id_form-0-answer_text">Answer text:</label></th>
    <td>
      
      <input type="text" name="form-0-answer_text" value="orage"  maxlength="250" id="id_form-0-answer_text">
      
      
    </td>
  </tr>

  <tr>
    <th><label for="id_form-0-is_correct">Is correct:</label></th>
    <td>
      
      <input type="checkbox" name="form-0-is_correct" id="id_form-0-is_correct">
      
      
        <input type="hidden" name="form-0-id" id="id_form-0-id">
      
    </td>
  </tr><tr>
    <th><label for="id_form-1-answer_text">Answer text:</label></th>
    <td>
      
      <input type="text" name="form-1-answer_text" value="apple"  maxlength="250" id="id_form-1-answer_text">
      
      
    </td>
  </tr>

  <tr>
    <th><label for="id_form-1-is_correct">Is correct:</label></th>
    <td>
      
      <input type="checkbox" name="form-1-is_correct" id="id_form-1-is_correct">
      
      
        <input type="hidden" name="form-1-id" id="id_form-1-id">
      
    </td>
  </tr><tr>
    <th><label for="id_form-2-answer_text">Answer text:</label></th>
    <td>
      
      <input type="text" name="form-2-answer_text"  maxlength="250" id="id_form-2-answer_text">
      
      
    </td>
  </tr>

  <tr>
    <th><label for="id_form-2-is_correct">Is correct:</label></th>
    <td>
      
      <input type="checkbox" name="form-2-is_correct" id="id_form-2-is_correct">
      
      
        <input type="hidden" name="form-2-id" id="id_form-2-id">
      
    </td>
  </tr><tr>
    <th><label for="id_form-3-answer_text">Answer text:</label></th>
    <td>
      
      <input type="text" name="form-3-answer_text"  maxlength="250" id="id_form-3-answer_text">
      
      
    </td>
  </tr>

  <tr>
    <th><label for="id_form-3-is_correct">Is correct:</label></th>
    <td>
      
      <input type="checkbox" name="form-3-is_correct" id="id_form-3-is_correct">
      
      
        <input type="hidden" name="form-3-id" id="id_form-3-id">
      
    </td>
  </tr>

print(instances) gives:

[<Answer: orage>, <Answer: apple>]

So, my question is: how to properly add data from forms, created in JS, to the database?

CodePudding user response:

Look at your post data you send form-1-answer_text for second form and for third you send form=2-answer_text with equal sign('=')

You need to correct yours js:

newForm.innerHTML = newForm.innerHTML.replace(formRegex, `form-${formNum}-`)
  • Related