You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用zip()函数处理答题数据插入时出错,求解决方案

问题:使用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

  1. You're not saving the TakenQuiz objects to the database
    This is the biggest problem: you create the z = TakenQuiz(...) object, but never call z.save() or use TakenQuiz.objects.create(...) to persist it. Without this, nothing gets written to your database.

  2. Risk of KeyError when fetching correct answers
    Your line correct_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.

  3. ForeignKey fields expect objects, not IDs (probably)
    If TakenQuiz.question and TakenQuiz.selected_choice are ForeignKey fields (which they should be for proper database relationships), passing x (a string ID) instead of a Question object will cause errors. We'll fetch the actual objects instead.

  4. 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_choice is an IntegerField (instead of a ForeignKey), you can replace selected_choice=selected_choice_obj with selected_choice=selected_choice_id to 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_404 instead of get to return a 404 if the ID is invalid).
  • The template had duplicate course entries in the context; I removed one to clean it up.

内容的提问来源于stack exchange,提问作者Olaniyan Adewale

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.12 05:04:06