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

Django多对多关联中如何仅更新特定考试的题目is_correct字段

问题根源与解决方案

你现在遇到的问题本质是模型设计逻辑错误——你把「用户在某次考试中是否答对这道题」这个属于「考试-用户-题目」关联场景的属性,错误地放到了Question模型里。Question的is_correct是全局字段,所有关联这道题的考试都会共享这个值,所以修改它自然会影响所有Exam。

要解决这个问题,我们需要利用Django多对多关系的中间模型(Through Model),把用户答题的正确性标记放到考试和题目的关联记录里,而不是题目本身。


步骤1:重构模型

首先,删除Question模型中的is_correct字段(因为它不属于题目本身),然后创建一个中间模型来保存考试与题目之间的额外数据:

from django.db import models
import uuid

# 保持Question的核心属性,移除is_correct
class Question(models.Model):
    topic = models.CharField(max_length=50, choices=TOPICS)
    title = models.CharField(max_length=50)
    text = models.CharField(max_length=200)
    LEVEL = (('EASY', 'EASY'), ('MEDIUM', 'MEDIUM'), ('HARD', 'HARD'))
    level = models.CharField(max_length=10, choices=LEVEL)
    # 选项与正确答案
    option1 = models.CharField(max_length=200, null=True)
    option2 = models.CharField(max_length=200, null=True)
    option3 = models.CharField(max_length=200, null=True)
    option4 = models.CharField(max_length=200, null=True)
    answer = models.CharField(max_length=200, null=True)  # 这是题目的固定正确答案

    def __str__(self):
        return f'{self.title} , {self.topic}'

# 中间模型:保存某场考试中某道题的答题结果
class ExamQuestion(models.Model):
    exam = models.ForeignKey(Exam, on_delete=models.CASCADE)
    question = models.ForeignKey(Question, on_delete=models.CASCADE)
    is_correct = models.BooleanField(default=False)  # 这个是当前考试下的答题对错
    user_choice = models.CharField(max_length=200, null=True)  # 可选:记录用户实际选的答案

    class Meta:
        unique_together = ('exam', 'question')  # 确保一场考试里同一道题只存一次结果

# 修改Exam模型,指定多对多关系使用中间模型
class Exam(models.Model):
    topic = models.CharField(max_length=50, choices=TOPICS)
    grade = models.IntegerField(default=0, null=True)
    STATUS = (('Pending', 'Pending'), ('Completed', 'Completed'))
    status = models.CharField(max_length=20, default='Pending', choices=STATUS)
    # 这里指定through参数关联中间模型
    questions = models.ManyToManyField(Question, through=ExamQuestion)
    exam_taker = models.ForeignKey(Candidate, null=True, on_delete=models.SET_NULL)
    exam_token = models.UUIDField(unique=True, default=uuid.uuid4, editable=False)
    exam_start_date = models.DateTimeField(auto_now_add=True, null=True, editable=False)
    exam_due_date = models.DateTimeField(null=True)

    def __str__(self):
        return f'{self.topic} | {self.exam_taker} | {self.exam_token} | {self.grade} | {self.status}'

步骤2:修改视图逻辑

现在不再直接修改Question的字段,而是操作ExamQuestion实例,确保只更新当前考试下的答题结果:

exam_data = Exam.objects.get(exam_token=exam_token)
if request.method == 'POST':
    choice = request.POST.get('answer')
    question_id = request.POST.get('question_id')
    
    # 获取目标题目
    question = Question.objects.get(id=question_id)
    correct_answer = question.answer
    
    # 获取或创建当前考试与该题的关联记录(避免重复创建)
    exam_question, created = ExamQuestion.objects.get_or_create(
        exam=exam_data,
        question=question
    )
    
    # 更新答题结果
    if choice == correct_answer:
        exam_question.is_correct = True
        exam_question.user_choice = choice
        exam_question.save()
        # 可选:更新考试分数
        # exam_data.grade += 10  # 假设每题10分
        # exam_data.save()
    else:
        exam_question.is_correct = False
        exam_question.user_choice = choice
        exam_question.save()

为什么这样能解决问题?

  • is_correct现在属于Exam-Question的关联实例,每个考试里的每道题都有独立的这个值,修改时只会影响当前考试的记录,不会全局改变Question的属性。
  • get_or_create方法确保我们不会为同一考试的同一题目创建多条记录,每次答题都更新同一个实例。

最后要做的事

  1. 运行数据库迁移命令,更新表结构:
    python manage.py makemigrations
    python manage.py migrate
    
  2. 如果之前有旧数据需要迁移,可以编写数据迁移脚本把原来的Question.is_correct值对应到每个Exam的ExamQuestion记录中(如果需要的话)。

内容的提问来源于stack exchange,提问作者Slava Bugz

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.09 07:52:42