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方法确保我们不会为同一考试的同一题目创建多条记录,每次答题都更新同一个实例。
最后要做的事
- 运行数据库迁移命令,更新表结构:
python manage.py makemigrations python manage.py migrate - 如果之前有旧数据需要迁移,可以编写数据迁移脚本把原来的
Question.is_correct值对应到每个Exam的ExamQuestion记录中(如果需要的话)。
内容的提问来源于stack exchange,提问作者Slava Bugz
相关产品推荐
相关产品推荐

