Data is shown before form submission in Django 4.1

community!

My goal is to create an app, which takes some user input, does machine-learning stuff and shows the results. I want to know whether the result was correct for each prediction. That's why I'm trying to implement a feedback system, where a user could mark the answer as "correct" or "incorrect" if one wants to do so.

To be exact, I have two buttons: "the prediction looks like true" and "the prediction was incorrect". After clicking on one of these buttons I have a pop-up dialog shown with 3 checkboxes, where a user can provide additional information (it is not required).

forms.py with these checkboxes looks like this:

from django import forms

class checkboxForm(forms.Form):
    is_winner_reason_true = forms.BooleanField(required=False, label="The first place in importance coincides with reality", label_suffix='')
    is_top5_reason_true = forms.BooleanField(required=False, label="Top 5 includes friends with high importance", label_suffix='')
    is_other_reason_true = forms.BooleanField(required=False, label="Other", label_suffix='')

class checkboxFormFalsePred(forms.Form):
    is_winner_reason_false = forms.BooleanField(required=False, label="The other person should come first in importance", label_suffix='')
    is_top5_reason_false = forms.BooleanField(required=False, label="Top 5 includes friends with low importance", label_suffix='')
    is_other_reason_false = forms.BooleanField(required=False, label="Other", label_suffix='')

I want to get data from clicking on one of two buttons (the value will be either True or False) and then, if a user decided to provide an additional feedback, to get data from that feedback too.

models.py looks like this:

from django.db import models
from allauth.socialaccount.models import SocialAccount

# Create your models here.
class ResultInfo(models.Model):
    uid = models.ForeignKey(SocialAccount, on_delete=models.CASCADE)
    friend_id = models.CharField(max_length = 16)
    status = models.BooleanField() # whether the answer is True or False
    status_description = models.CharField(max_length = 16, null=True, blank=True) # the additional info about the answer
    result_info = models.JSONField()

Note that status_description is intended to get additional data from 3 checkboxes. Originally, it comes as a dictionary and looks like {'is_winner_reason_true': False, 'is_top5_reason_true': False, 'is_other_reason_true': False}. Later, I will transform it to ONE category for storing it in database as a column status_description.

I created AJAX calls for forms in order to prevent refreshing the page after the form is submitted. It does its job, although with 2-second delay.

Here goes my piece of HTML:

...

<button class="looks-like-true" value="True" name="looks-like-true">
    <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><path d="M470.6 105.4c12.5 12.5 12.5 32.8 0 45.3l-256 256c-12.5 12.5-32.8 12.5-45.3 0l-128-128c-12.5-12.5-12.5-32.8 0-45.3s32.8-12.5 45.3 0L192 338.7 425.4 105.4c12.5-12.5 32.8-12.5 45.3 0z"/></svg>
    <span class="looks-like-true-content">
        Looks like true
    </span>
</button>

<button class="false-prediction" value="False" name="false-prediction">
    <span class="false-prediction-content">
        Prediction is incorrect
    </span>
</button>

<dialog class="verification-modal" id="verify-true">
    <p>Thanks for the feedback! What do you like about the prediction? </p><p>(Answer is optional, but more information would help us improve the predictive model)</p>
    <div class="modal-checkboxes">
        <form action="" method="POST" id="form-verify-true">{% csrf_token %}
        {{res_verif_form.as_p}}
        <input type="submit" value="Send" class="show-more-close-button">
        </form>
    </div>  
</dialog>

<dialog class="verification-modal" id="verify-false">
    <p>Thanks for the feedback! What did not you like about the prediction? </p><p>(Answer is optional, but more information would help us improve the predictive model)</p>
    <div class="modal-checkboxes">
        <form action="" method="POST" id="form-verify-false">{% csrf_token %}
        {{res_verif_form_false_pred.as_p}}
        <input type="submit" value="Send" class="show-more-close-button">
        </form>
    </div>
</dialog>

...
<script src="https://code.jquery.com/jquery-3.6.0.js" integrity="sha256-H+K7U5CnXl1h5ywQfKtSj8PCmoN9aaq30gDh27Xc0jk=" crossorigin="anonymous"></script>

<script>
const verifyModal = document.querySelector('#verify-true')
const verifyModalFalse = document.querySelector('#verify-false')

$(document).ready(
  $('#form-verify-true').submit(function(e){
    e.preventDefault();
    var serializedData = $(this).serialize();

    $.ajax({
      type:"POST",
      url: "./",
      data: serializedData,
      success: function(response){
        $("#form-verify-true").trigger('reset');
        verifyModal.close();    
      }
    });
  })
);

$(document).ready(
  $('#form-verify-false').submit(function(e){
    e.preventDefault();
    var serializedData = $(this).serialize();

    $.ajax({
      type:"POST",
      url: "./",
      data: serializedData,
      success: function(response){
        $("#form-verify-false").trigger('reset');
        verifyModalFalse.close();    
      }
    });
  })
);

$(document).ready(
  $('.looks-like-true').click(function(){

    $.ajax({
      type:"POST",
      url: "./",
      data: {'content_id': $(this).attr('name'),'operation':'ans_submit','csrfmiddlewaretoken': '{{ csrf_token }}'},
      success: function(response){
        console.log("Success") 
      }
    });
  })
);

$(document).ready(
  $('.false-prediction').click(function(){

    $.ajax({
      type:"POST",
      url: "./",
      data: {'content_id': $(this).attr('name'),'operation':'ans_submit','csrfmiddlewaretoken': '{{ csrf_token }}'},
      success: function(response){
        console.log("Success") 
      }
    });
  })
);
</script>

A piece of code from views.py:

if (request.method == 'POST') and (request.POST.get('content_id', None) == "looks-like-true"):
        res_verif_form = checkboxForm(request.POST)
        if (res_verif_form.is_valid()):
            print(res_verif_form.cleaned_data)
            return HttpResponse('success')

    if (request.method == 'POST') and (request.POST.get('content_id', None) == "false-prediction"):
        res_verif_form_false_pred = checkboxFormFalsePred(request.POST)
        if (res_verif_form_false_pred.is_valid()):
            print(res_verif_form_false_pred.cleaned_data)


    res_verif_form = checkboxForm()
    res_verif_form_false_pred = checkboxFormFalsePred()

Now, here comes the problem.

The data from additional questions is getting printed out after clicking on one of the two buttons, but BEFORE the form with these additional questions is submitted. Besides, after I submit a form I get no printed information at all. Just "POST /id4564365/results/ HTTP/1.1" 200 86111 in my cmd.

I tried to remove these lines:

$(document).ready(
  $('.looks-like-true').click(function(){

    $.ajax({
      type:"POST",
      url: "./",
      data: {'content_id': $(this).attr('name'),'operation':'ans_submit','csrfmiddlewaretoken': '{{ csrf_token }}'},
      success: function(response){
        console.log("Success") 
      }
    });
  })
);

$(document).ready(
  $('.false-prediction').click(function(){

    $.ajax({
      type:"POST",
      url: "./",
      data: {'content_id': $(this).attr('name'),'operation':'ans_submit','csrfmiddlewaretoken': '{{ csrf_token }}'},
      success: function(response){
        console.log("Success") 
      }
    });
  })
);

Then I changed views.py to look like this:

if (request.method == 'POST'):
        res_verif_form = checkboxForm(request.POST)
        if (res_verif_form.is_valid()):
            print(res_verif_form.cleaned_data)
            return HttpResponse('success')

    if (request.method == 'POST'):
        res_verif_form_false_pred = checkboxFormFalsePred(request.POST)
        if (res_verif_form_false_pred.is_valid()):
            print(res_verif_form_false_pred.cleaned_data)

Well, it worked. My data is now printed after the form submission. But the problem is that I don't get the data from the first two buttons.

change the submit input in the modal form to a normal button:

<input type="submit" value="Send" class="show-more-close-button">

<button class="show-more-close-button">Send</button>
~~
and link the eventListener to that button

There are a few ways to solve this. The solution I am going to give you involves AJAX although its not necessarily required to to what you want. And the reason is because it is what you are trying to use and because its easier to obtain the desired output.

I will start with forms.py that seems redundant. reason is a variable based on user input provided by buttons, since it possible to access that variable and labels can be handled using JavaScript, there is no need for two forms:

class checkBoxForm(forms.Form):
    is_winner = forms.BooleanField(
        required=False, 
        label="The first place in importance coincides with reality", 
        label_suffix=''
    )

    is_top5 = forms.BooleanField(
        required=False, 
        label="Top 5 includes friends with high importance", 
        label_suffix=''
    )

    is_other = forms.BooleanField(
        required=False, 
        label="Other", 
        label_suffix=''
    )

Even with some alterations in template.html I tried to preserve as much as I could from your original code (although, honestly I would change some names).

In this following HTML part, checkBoxForm is used to easily render modal inputs as you were originally doing, but note that the form is not being submitted yet:

<button class="looks-like-true" name="looks-like-true">
    <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><path d="M470.6 105.4c12.5 12.5 12.5 32.8 0 45.3l-256 256c-12.5 12.5-32.8 12.5-45.3 0l-128-128c-12.5-12.5-12.5-32.8 0-45.3s32.8-12.5 45.3 0L192 338.7 425.4 105.4c12.5-12.5 32.8-12.5 45.3 0z"/></svg>
    <span class="looks-like-true-content">
        Looks like true
    </span>
</button>

<button class="false-prediction" name="false-prediction">
    <span class="false-prediction-content">
        Prediction is incorrect
    </span>
</button>

<dialog class="verification-modal">
    <p>Thanks for the feedback! What do you like about the prediction? </p><p>(Answer is optional, but more information would help us improve the predictive model)</p>
    <div class="modal-checkboxes">
        {{form.as_p}}
        <button id="send-form" >Send</button>
    </div>  
</dialog>

Instead, form is submitted via JavaScript (jQuery) by collecting data and sending it using AJAX. Obtaining csrftoken by following documentation on how to use CSRF protection with AJAX and properly setting it on the request header:

<script src="https://code.jquery.com/jquery-3.6.0.js" integrity="sha256-H+K7U5CnXl1h5ywQfKtSj8PCmoN9aaq30gDh27Xc0jk=" crossorigin="anonymous"></script>

<script>
  const verifyModal = document.querySelector('.verification-modal');
  let reason = null;

  function getCookie(name) {
    ...
  }

  $('.looks-like-true').click(function() {
    reason = true;
    $("label[for='id_is_winner']").text("The first place in importance coincides with reality");
    $("label[for='id_is_top5']").text("Top 5 includes friends with high importance");
    verifyModal.showModal();
  });

  $('.false-prediction').click(function() {
    reason = false;
    $("label[for='id_is_winner']").text("The other person should come first in importance");
    $("label[for='id_is_top5']").text("Top 5 includes friends with low importance");
    verifyModal.showModal();
  });
  
  $('#send-form').click(function() {
    is_winner = document.querySelector('#id_is_winner').checked;
    is_top5 = document.querySelector('#id_is_top5').checked;
    is_other = document.querySelector('#id_is_other').checked;

    url = '/your/url/'
    const csrftoken = getCookie('csrftoken');
    headers = {'X-CSRFToken': csrftoken, 'Content-Type': 'application/json'}
    data = {
      'reason': reason,
      'is_winner': is_winner,
      'is_top5': is_top5,
      'is_other': is_other,
    }

    $.ajax({
      type:"POST",
      url: url,
      headers: headers,
      data: JSON.stringify(data),
      success: function(response){
        console.log(response.msg);
        verifyModal.close();
        // reload or redirect or update page using JS...
        location.reload();
        // window.location.replace("https://stackoverflow.com/a/506004/7100120");
      }
    });
  });
</script>

views.py

from .forms import checkBoxForm
from django.http import JsonResponse
import json

def your_view_name(request):
    if request.method == 'POST':
        data = json.loads(request.body)

        reason = data.pop('reason')

        print(f'Reason: {reason}')
        print(f'Status Description: {data}')

        # Output Sample
        # Reason: True
        # Status Description: {'is_winner': True, 'is_top5': False, 'is_other': True}

        return JsonResponse({'msg': 'success'})

    form = checkBoxForm()

    context = {
        'form': form,
    }

    return render(request, 'your_template.html', context)
Back to Top