使用zip()函数处理答题数据插入时出错,求解决方案
Hey there, let's break down what's going wrong with your code and fix it step by step. From what I can see, you're trying to pair question IDs with selected answer IDs using zip(), then save whether each answer was correct to the TakenQuiz model—but there are a few critical issues stopping this from working properly.
First, let's look at your original code snippets:
View Function
@login_required @student_required def take_exam(request, pk): course = get_object_or_404(Course, pk=pk) student = request.user.student question = course.questions.filter() #correct_answers = student.course_answers.filter(answer__question__quiz=course, answer__is_correct=True).count() total_questions = course.questions.count() choice = Answer.objects.filter() marks_obtainable = Details.objects.get(course_id=course) if request.method == 'POST': question_pk = request.POST.getlist('question_pk') #question_obj = Question.objects.filter(id=int(question_pk)) #question_obj = Question.objects.filter(id=question_pk) #choice_pk = [request.POST['choice_pk{}'.format(q)] for q in question_obj] choice_pk = [request.POST['choice_pk{}'.format(q)] for q in question_pk] #print(marks_obtainable.marks_obtained) zipped = zip(question_pk, choice_pk) for x, y in zipped: correct_answers = Answer.objects.filter(question_id=x, is_correct=True).values("id").first()['id'] print(x, y, correct_answers) if int(y) == int(correct_answers): z = TakenQuiz(student=student, course=course, question=x, selected_choice=y, marks_obtained=marks_obtainable, is_correct=True) print("correct") else: z = TakenQuiz(student=student, course=course, question=x, selected_choice=y, marks_obtained=marks_obtainable, is_correct=False) print("Not Correct") return render(request, 'classroom/students/take_exam_form.html', { 'course': course, 'question': question, 'course': course, 'total_questions': total_questions, 'choice': choice, 'marks_obtainable': marks_obtainable })
Template
<form method="post" novalidate> {% csrf_token %} {% for questions in question %} <input type="hidden" name="question_pk" value="{{ questions.pk }}"> <h3 class="text-info">{{ questions.text|safe }}</h3> {% for choices in questions.answers.all %} <input class="form-check-input" type="radio" name="choice_pk{{ questions.pk }}" id="choices-{{ forloop.counter }}" value="{{ choices.pk }}"> <label class="form-check-label" for="choices-{{ forloop.counter }}"> {{ choices.text|safe }} </label> {% endfor %} {% endfor %} <button type="submit" class="btn btn-primary">Submit Now →</button> </form>
Key Issues & Fixes
You're not saving the
TakenQuizobjects to the database
This is the biggest problem: you create thez = TakenQuiz(...)object, but never callz.save()or useTakenQuiz.objects.create(...)to persist it. Without this, nothing gets written to your database.Risk of KeyError when fetching correct answers
Your linecorrect_answers = Answer.objects.filter(question_id=x, is_correct=True).values("id").first()['id']will throw a KeyError if a question has no correct answer (even if this shouldn't happen, it's a bad edge case to ignore). We'll add a check to handle this.ForeignKey fields expect objects, not IDs (probably)
IfTakenQuiz.questionandTakenQuiz.selected_choiceare ForeignKey fields (which they should be for proper database relationships), passingx(a string ID) instead of aQuestionobject will cause errors. We'll fetch the actual objects instead.Redundant database queries
You're querying for the correct answer inside the loop, which hits the database once per question. We'll optimize this by fetching all correct answers upfront.
Fixed Code
Updated View Function
@login_required @student_required def take_exam(request, pk): course = get_object_or_404(Course, pk=pk) student = request.user.student questions = course.questions.all() # Renamed to plural for clarity total_questions = questions.count() marks_obtainable = Details.objects.get(course_id=course) if request.method == 'POST': question_pks = request.POST.getlist('question_pk') choice_pks = [request.POST[f'choice_pk{q}'] for q in question_pks] # Pre-fetch all correct answers to avoid repeated DB hits correct_answer_map = {} for q_id in question_pks: correct_answer = Answer.objects.filter(question_id=q_id, is_correct=True).first() if correct_answer: correct_answer_map[q_id] = correct_answer.id # Pair questions and selected answers for q_id, selected_choice_id in zip(question_pks, choice_pks): # Skip if no correct answer exists for this question if q_id not in correct_answer_map: print(f"No correct answer found for question {q_id}") continue # Check if the selected answer is correct is_correct = int(selected_choice_id) == correct_answer_map[q_id] # Fetch the actual objects for ForeignKey fields question_obj = Question.objects.get(id=q_id) selected_choice_obj = Answer.objects.get(id=selected_choice_id) # Create AND save the TakenQuiz record in one step TakenQuiz.objects.create( student=student, course=course, question=question_obj, selected_choice=selected_choice_obj, marks_obtained=marks_obtainable, is_correct=is_correct ) print("Correct!" if is_correct else "Not Correct") return render(request, 'classroom/students/take_exam_form.html', { 'course': course, 'questions': questions, # Updated variable name to match plural 'total_questions': total_questions, 'marks_obtainable': marks_obtainable })
Updated Template (cleaned up variable names)
<form method="post" novalidate> {% csrf_token %} {% for question in questions %} <input type="hidden" name="question_pk" value="{{ question.pk }}"> <h3 class="text-info">{{ question.text|safe }}</h3> {% for choice in question.answers.all %} <input class="form-check-input" type="radio" name="choice_pk{{ question.pk }}" id="choices-{{ forloop.counter }}" value="{{ choice.pk }}"> <label class="form-check-label" for="choices-{{ forloop.counter }}"> {{ choice.text|safe }} </label> {% endfor %} {% endfor %} <button type="submit" class="btn btn-primary">Submit Now →</button> </form>
Additional Notes
- If
TakenQuiz.selected_choiceis an IntegerField (instead of a ForeignKey), you can replaceselected_choice=selected_choice_objwithselected_choice=selected_choice_idto avoid fetching the Answer object. - Consider adding error handling for cases where a question or answer ID doesn't exist (e.g., using
get_object_or_404instead ofgetto return a 404 if the ID is invalid). - The template had duplicate
courseentries in the context; I removed one to clean it up.
内容的提问来源于stack exchange,提问作者Olaniyan Adewale

