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