Data from forms not saved in database (Django)

The information I enter on my Website when I want to create a new quiz is only partially saved, the data for the Quiz model is saved in my database, but the Questions with Answers are not saved in the database when creating a quiz. Could this be a problem with the routes? Because the Quiz model is in quizes models.py, and the Answers and Questions are in questions models.py

[20/Oct/2024 18:57:06] "GET / HTTP/1.1" 200 15893
[20/Oct/2024 18:57:06] "GET /static/css/style.css HTTP/1.1" 404 1966
[20/Oct/2024 18:57:06] "GET /static/js/script.js HTTP/1.1" 404 1963
[20/Oct/2024 18:57:06] "GET /favicon.ico/ HTTP/1.1" 204 0
[20/Oct/2024 18:57:06] "GET /add_quiz HTTP/1.1" 200 26109
[20/Oct/2024 18:57:06] "GET /static/js/script.js HTTP/1.1" 404 1963
[20/Oct/2024 18:57:06] "GET /static/css/style.css HTTP/1.1" 404 1966
[20/Oct/2024 18:57:07] "GET /favicon.ico/ HTTP/1.1" 204 0
[]
[20/Oct/2024 18:57:25] "POST /add_quiz HTTP/1.1" 302 0
[20/Oct/2024 18:57:25] "GET / HTTP/1.1" 200 16290
[20/Oct/2024 18:57:25] "GET /static/js/script.js HTTP/1.1" 404 1963
[20/Oct/2024 18:57:25] "GET /static/css/style.css HTTP/1.1" 404 1966
[20/Oct/2024 18:57:25] "GET /favicon.ico/ HTTP/1.1" 204 0

also console returns me empty list

Earlier python console return [{'id': ['This field is required.']}, {'id': ['This field is required.']}, {'id': ['This field is required.']}, {}] but it fixed when I hide management_form


**Attach my code below**

**add_quiz.html**
{% extends 'base.html' %}

{% block content %}
    <div class="container">
        <h2>Create a New Quiz</h2>

        <!-- Форма для создания квиза -->
        <form action="{% url 'quizes:add-quiz-view' %}" method="post" enctype="multipart/form-data">
            {% csrf_token %}

            <!-- Основная форма для квиза -->
            <div class="form-error">{{ form.non_field_errors }}</div>
            {% for field in form %}
                <div class="form-group">
                    <label for="{{ field.id_for_label }}">{{ field.label }}:</label>
                    {{ field }}
                    <div class="form-error">{{ field.errors }}</div>
                </div>
            {% endfor %}

            <!-- Важно для корректной работы formset вопросов -->
{#            {{ question_formset.management_form }}#}

            <!-- Генерация вопросов через formset -->
            <div id="questions-container">
                {% for question_form in question_formset %}
                    <div class="question-block">
                        <h4>Question {{ forloop.counter }}</h4>

                        <!-- Поле для текста вопроса -->
                        <div class="form-group">
                            {{ question_form.question_type }}
                            {{ question_form.question_text.label_tag }}
                            {{ question_form.question_text }}
                            <div class="form-error">{{ question_form.question_text.errors }}</div>
                        </div>

                        <!-- Важно для корректной работы formset ответов -->
                        {{ question_form.answer_formset.management_form }}

                        <!-- Генерация ответов для каждого вопроса (по 3 ответа) -->
                        <div class="answers-container">
                            {% for answer_form in question_form.answer_formset %}
                                <div class="form-group">
                                    <label for="{{ answer_form.answer_text.id_for_label }}">
                                        Answer {{ forloop.counter }}
                                    </label>
                                    {{ answer_form.answer_text }}
                                    <div class="form-error">{{ answer_form.answer_text.errors }}</div>

                                    <div class="form-check">
                                        <input class="form-check-input" type="checkbox"
                                               name="{{ answer_form.is_correct.name }}"
                                               id="{{ answer_form.is_correct.id_for_label }}">
                                        <label class="form-check-label"
                                               for="{{ answer_form.is_correct.id_for_label }}">
                                            Correct
                                        </label>
                                    </div>
                                    <div class="form-error">{{ answer_form.is_correct.errors }}</div>
                                </div>
                            {% endfor %}
                        </div>
                    </div>
                {% endfor %}
            </div>

            <button type="submit" class="btn btn-primary">Create Quiz</button>
        </form>
    </div>
{% endblock %}

views.py

class create_quiz_view(DataMixin, CreateView):
    form_class = QuizForm
    template_name = 'quizes/add_quiz.html'
    success_url = reverse_lazy('quizes:main-view')

    def get_context_data(self, **kwargs):
        context = super().get_context_data(**kwargs)
        c_def = self.get_user_context(title="Add Quiz")

        # Инициализируем вопросный formset
        question_formset = QuestionFormSet(prefix='questions')

        # Для каждого вопроса инициализируем formset для ответов
        for question_form in question_formset:
            question_form.answer_formset = AnswerFormSet(prefix=f'answers-{question_form.prefix}')

        # Передаем formset вопросов и ответов в контекст
        context['question_formset'] = question_formset
        return {**context, **c_def}

    def form_valid(self, form):
        # Сохраняем квиз
        self.object = form.save()

        # Получаем formset вопросов
        question_formset = QuestionFormSet(self.request.POST, prefix='questions')
        print(question_formset.errors)  # Вывод ошибок формсета вопросов

        if question_formset.is_valid():
            # Сохраняем вопросы и ответы
            for question_form in question_formset:
                question = question_form.save(commit=False)
                question.quiz = self.object  # Привязываем вопрос к созданному квизу
                question.save()

                # Получаем formset ответов для текущего вопроса
                answer_formset = AnswerFormSet(self.request.POST, instance=question,
                                               prefix=f'answers-{question_form.prefix}')

                if answer_formset.is_valid():
                    # Сохраняем все ответы для данного вопроса
                    answer_formset.save()
                    print(self.request.POST)

        return redirect(self.get_success_url())  # Перенаправляем после успешного сохранения



    def form_invalid(self, form):
        # Если форма не валидна, просто возвращаем ошибки
        print(form.errors)  # Для отладки
        return self.render_to_response(self.get_context_data(form=form))









def quiz_view(request, pk):
    if pk == 'favicon.ico':
        return HttpResponse(status=204)
    quiz = Quiz.objects.get(pk=pk)
    return render(request, 'quizes/quiz.html', {'obj': quiz})


def quiz_data_view(request, pk):
    try:
        quiz = Quiz.objects.get(pk=pk)
        questions = []

        # Отладочные сообщения
        print(f'Quiz: {quiz.title}, Time: {quiz.time}')

        # Получаем все вопросы викторины
        quiz_questions = quiz.questions.all()
        print(f'Questions Count: {quiz_questions.count()}')

        for question in quiz_questions:
            answers = [answer.answer_text for answer in question.answers.all()]
            print(f'Question: {question.question_text}, Answers: {answers}')
            questions.append({str(question.question_text): answers})

        return JsonResponse({
            'data': questions,
            'time': quiz.time
        })
    except Quiz.DoesNotExist:
        return JsonResponse({'error': 'Quiz not found'}, status=404)
    except Exception as e:
        return JsonResponse({'error': str(e)}, status=500)


def save_quiz_view(request, pk):
    if request.accepts("application/json"):
        questions = []
        data = request.POST
        data_ = dict(data.lists())

        data_.pop('csrfmiddlewaretoken')

        for k in data_.keys():
            print('key: ', k)
            question = Question.objects.get(question_text=k)
            questions.append(question)
        print(questions)

        user = request.user
        quiz = Quiz.objects.get(pk=pk)

        required_score = quiz.required_score
        score = 0
        multiplier = 100 / len(questions)
        results = []

        for q in questions:
            a_selected = data[q.question_text]

            if a_selected != '':
                correct_answer = Answer.objects.filter(question=q).get(is_correct=True)
                if a_selected == correct_answer.answer_text:
                    score += 1

                results.append({q.question_text: {
                    'correct_answer': correct_answer.answer_text,
                    'answered': a_selected
                }})
            else:
                results.append({q.qustion_text: 'not answered'})

        final_score = score * multiplier

        Result.objects.create(quiz=quiz, user=user, score=final_score)

        json_response = {
            'score': final_score,
            'correct_questions': score,
            'passed': False,
            'required_score': required_score,
            'results': results
        }

        if final_score >= required_score:
            json_response['passed'] = True
            return JsonResponse(json_response)

        return JsonResponse(json_response)

forms.py

class QuizForm(forms.ModelForm):
    class Meta:
        model = Quiz
        fields = ['title', 'description', 'time', 'required_score', 'difficulty']

    def clean_title(self):
        title = self.cleaned_data.get('title')
        if len(title) > 64:
            raise ValidationError('Quiz title is too long.')
        return title

    def clean_required_score(self):
        required_score = self.cleaned_data.get('required_score')
        if required_score > 100:
            raise ValidationError('Maximum score is 100%.')
        return required_score

# Форма для создания Question
class QuestionForm(forms.ModelForm):
    class Meta:
        model = Question
        fields = ['question_text', 'question_type']  # id не включаем

    def clean_question_text(self):
        text = self.cleaned_data.get('question_text')
        if len(text) > 64:  # ограничение на длину вопроса
            raise ValidationError('Question text is too long.')
        return text

class AnswerForm(forms.ModelForm):
    class Meta:
        model = Answer
        fields = ['answer_text', 'is_correct']  # id не включаем

    def clean_answer_text(self):
        text = self.cleaned_data.get('answer_text')
        if len(text) == 0:
            raise ValidationError('Answer text cannot be empty.')
        return text

# Формсеты для вопросов, для создания нескольких вопросов
QuestionFormSet = modelformset_factory(Question, form=QuestionForm, extra=1)

# Формсеты для вложенных ответов в каждом вопросе
AnswerFormSet = inlineformset_factory(Question, Answer, form=AnswerForm, extra=3, can_delete=True)


quiz.js

$.ajax({
    type: 'GET',
    url: `${url}data/`,
    success: function (response) {
        // console.log(response)
        const data = response.data
        data.forEach(el => {
            for (const [question, answers] of Object.entries(el)){
                quizBox.innerHTML  += `
                    <hr>
                    <div class="mb-2">
                        <b>${question}</b>
                    </div>
                `
                answers.forEach(answer=> {
                    quizBox.innerHTML += `
                        <div>
                            <input type="radio" class="ans" id="${question}-${answer}" name="${question}" value="${answer}">
                            <label for="${question}">${answer}</label>
                        </div>
                    `
                })
            }
        })
        activateTimer(response.time)
    },
    error: function (error) {
        console.log(error)
    }
})

const quizForm = document.getElementById('quiz-form')
const csrf = document.getElementsByName('csrfmiddlewaretoken')

const sendData = () => {

    const elements = [...document.getElementsByClassName('ans')]
    const data = {}
    data['csrfmiddlewaretoken'] = csrf[0].value
    elements.forEach(el => {
        if (el.checked) {
            data[el.name] = el.value
        } else {
            if (!data[el.name]){
                data[el.name] = null
            }
        }
    })

   $.ajax({
    type: 'POST',
    url: `${url}/save`,
    data: data,
    success: function(response){
        const results = response.results
        const n_correct_answers = response.score
        const score = response.score.toFixed(2)
        const passed = response.passed

        // Removes the form
        quizForm.remove()

        let scoreDiv = document.createElement('div')
        // scoreDiv.classList.add(...['container', 'my-auto', 'text-secondary'])


        scoreDiv.innerHTML += `
                           <p> ${passed ? 'Congrats you passed the test!' : 'Sorry, you did not pass the test!'} Your result is ${score} %</p>
                           <p> Answered correctly: ${n_correct_answers}</p>
                           `

        scoreBox.append(scoreDiv)

        results.forEach(res =>{
            let resDiv = document.createElement('div')

            for (const [question, resp] of Object.entries(res)){
                resDiv.innerHTML += question

                const classes = ['container', 'p-3', 'text-light', 'h4']
                resDiv.classList.add(...classes)

                if (resp == 'not answered'){
                    resDiv.innerHTML += ' — Not answered'
                    resDiv.classList.add('bg-danger')
                } else{
                    const answer = resp['answered']
                    const correct = resp['correct_answer']

                    if (answer == correct){
                        resDiv.classList.add('bg-success')
                        resDiv.innerHTML += ` Answered: ${answer}`
                    } else {
                        resDiv.classList.add('bg-danger')
                        resDiv.innerHTML += `| Answered: ${answer}`
                        resDiv.innerHTML += `| Correct answer: ${correct}`
                    }
                }
            }
            resultBox.append(resDiv)
        })
    },
    error: function(error){
        console.log(error)
    }
})
}

quizForm.addEventListener('submit', e=> {
    e.preventDefault()

    sendData()
})

Please help, can not solve the problem for 5 days, I am novice in Django request more information, try to provide as soon as possible

Back to Top