Данные отображаются перед отправкой формы в Django 4.1

общество!

Моя цель - создать приложение, которое принимает некоторые данные от пользователя, выполняет машинное обучение и показывает результаты. Я хочу знать, был ли результат правильным для каждого предсказания. Поэтому я пытаюсь реализовать систему обратной связи, где пользователь мог бы отметить ответ как "правильный" или "неправильный", если он хочет это сделать.

Точнее говоря, у меня есть две кнопки: "предсказание похоже на истинное" и "предсказание было неверным". После нажатия на одну из этих кнопок у меня появляется всплывающий диалог с 3 чекбоксами, где пользователь может предоставить дополнительную информацию (это не обязательно).

forms.py с этими флажками выглядит следующим образом:

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='')

Я хочу получить данные от нажатия на одну из двух кнопок (значение будет либо True, либо False), а затем, если пользователь решил оставить дополнительный отзыв, получить данные и от этого отзыва.

models.py выглядит следующим образом:

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()

Обратите внимание, что status_description предназначен для получения дополнительных данных из 3 флажков. Первоначально он поставляется в виде словаря и выглядит как {'is_winner_reason_true': False, 'is_top5_reason_true': False, 'is_other_reason_true': False}. Позже я преобразую его в ОДНУ категорию для хранения в базе данных в виде столбца status_description.

Я создал AJAX вызовы для форм, чтобы предотвратить обновление страницы после отправки формы. Это делает свою работу, хотя и с 2-секундной задержкой.

Вот мой кусок HTML:

Кусок кода из 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()

Теперь возникает проблема.

Данные из дополнительных вопросов распечатываются после нажатия на одну из двух кнопок, но ДО отправки формы с этими дополнительными вопросами. Кроме того, после отправки формы я вообще не получаю никакой печатной информации. Только "POST /id4564365/results/ HTTP/1.1" 200 86111 в моей cmd.

Я пытался удалить эти строки:

$(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") 
      }
    });
  })
);

Затем я изменил views.py на следующий вид:

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)

Что ж, это сработало. Теперь мои данные печатаются после отправки формы. Но проблема в том, что я не получаю данные от первых двух кнопок.

замените вход submit в модальной форме на обычную кнопку:

<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

Есть несколько способов решить эту проблему. Решение, которое я собираюсь предложить вам, включает в себя AJAX, хотя он не обязательно требуется для того, что вы хотите. Причина в том, что это именно то, что вы пытаетесь использовать, и потому что так проще получить желаемый результат.

Я начну с forms.py, который кажется излишним. reason - это переменная, основанная на пользовательском вводе, предоставляемом кнопками, поскольку к этой переменной можно получить доступ, а метки можно обрабатывать с помощью JavaScript, нет необходимости в двух формах:

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=''
    )

Даже с некоторыми изменениями в template.html я старался сохранить как можно больше от вашего оригинального кода (хотя, честно говоря, я бы изменил некоторые имена).

В следующей части HTML, checkBoxForm используется для простого отображения модальных входов, как вы делали изначально, но обратите внимание, что форма еще не отправлена:

<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>

Вместо этого форма отправляется через JavaScript (jQuery), собирая данные и отправляя их с помощью AJAX. Получение csrftoken путем следования документации о как использовать CSRF защиту с AJAX и правильной установки ее на запрос 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)
Вернуться на верх