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

Django月度测验应用用户答题响应存储方案设计咨询

JSON文件存储答题记录方案的缺陷
  • 一致性无保障:答题提交过程中如果出现进程中断、服务宕机,JSON文件很容易出现半写入的格式损坏,单文件内存储的多月份答题记录可能全部丢失,无法像数据库一样通过事务机制保证写入操作要么全部成功、要么全部回滚。
  • 并发写入易冲突:如果用户同时打开多个答题页面、或后台任务同时更新用户答题状态,多进程同时操作同一个JSON文件会直接出现内容覆盖,最终留存的答题记录错乱,若自行实现文件锁机制,会带来额外的性能开销和逻辑漏洞风险。
  • 查询统计效率极低:做错题率统计、用户得分计算、单场测验参与情况汇总这类操作时,无法直接通过数据库筛选,需要遍历读取所有用户的JSON文件逐个解析,用户量上涨后磁盘IO开销会直接拖垮服务,也无法通过索引实现快速查询。
  • 数据校验能力缺失:JSON文件内存储的题目ID、选项ID无法和数据库内的题目、选项数据做联动校验,一旦题目被删除、调整,文件内会留存大量无效脏数据,后续清理、核对的成本极高。
  • 运维复杂度高:JSON文件存储在本地磁盘,多节点部署、服务迁移、数据备份时需要单独同步文件资源,远不如数据库天然支持主从同步、统一备份的机制可靠,很容易出现文件漏同步导致的数据丢失。
可落地的数据库存储优化方案

你目前编写的UserQuizAnswer模型已经符合关系型存储的核心思路,只需要修正现有模型的逻辑bug、补全缺失的关联关系即可,完全可以满足全量答题记录留存的需求,后续导出JSON做数据分析也比读文件更灵活:

  1. 模型层面的调整点
    • 给Answer模型添加关联题目的外键,明确每个选项对应的所属题目
    • 给UserQuizAnswer添加关联测验的外键,不需要通过题目间接关联所属测验,方便快速筛选单场测验的所有答题记录
    • 删除Question模型中冗余的month、year字段,这类信息可以通过关联的Quiz外键直接获取,避免重复存储带来的数据不一致
    • 修正Quiz模型save方法的逻辑错误,现有判断逻辑在quiz_name为空时拼接内容的写法完全无效
    • 修正UserQuizAnswer的字段拼写错误(selcted_index应为selected_index)
  2. 优化后的参考模型代码
from django.db import models
from django.contrib.auth.models import User
import uuid

class Quiz(models.Model):
    id = models.AutoField(primary_key=True)
    quiz_name = models.CharField(max_length=500)
    month = models.CharField(max_length=250, null=True, blank=True)
    year = models.CharField(max_length=250, null=True, blank=True)

    def __str__(self):
        return f"{self.quiz_name}-{self.month}-{self.year}"

    def save(self, *args, **kwargs):
        # 修正原save方法的逻辑错误:自动生成测验名称时判断名称是否未设置
        if not self.quiz_name:
            self.quiz_name = f"月度测验-{self.month}-{self.year}"
        super().save(*args, **kwargs)


class Question(models.Model):
    id = models.AutoField(primary_key=True)
    question = models.CharField(max_length=500)
    quiz = models.ForeignKey(Quiz, on_delete=models.CASCADE)
    # 删除冗余的month、year字段,通过quiz关联获取即可

    def __str__(self):
        return f"{self.question}-{self.quiz.month}-{self.quiz.year}"


class Answer(models.Model):
    id = models.AutoField(primary_key=True)
    answer = models.CharField(max_length=500)
    score = models.IntegerField()
    # 新增外键关联所属题目
    question = models.ForeignKey(Question, on_delete=models.CASCADE, related_name="options")
    # 新增选项索引字段,和用户选择的索引对应
    option_index = models.IntegerField()

    def __str__(self):
        return self.answer


class UserQuizAnswer(models.Model):
    options = (
        (0, 0), (1, 1), (2, 2), (3, 3)
    )
    id = models.AutoField(primary_key=True)
    question = models.ForeignKey(Question, on_delete=models.CASCADE)
    # 新增外键关联所属测验,方便快速筛选单场测验记录
    quiz = models.ForeignKey(Quiz, on_delete=models.CASCADE)
    user = models.ForeignKey(User, on_delete=models.CASCADE)
    # 修正原字段拼写错误
    selected_index = models.IntegerField(choices=options)
    # 可选:新增答题时间、是否正确等冗余字段,减少后续统计时的关联查询
    submit_time = models.DateTimeField(auto_now_add=True)
    is_correct = models.BooleanField(default=False)

    class Meta:
        # 添加联合索引,大幅提升查询效率
        indexes = [
            models.Index(fields=['user', 'quiz']),
            models.Index(fields=['quiz', 'question']),
        ]

    def __str__(self):
        return f"{self.user.username}-{str(uuid.uuid4())[:10]}"


class StudentFile(models.Model):
    id = models.AutoField(primary_key=True)
    name = models.TextField(null=True, blank=True)
    student = models.ForeignKey(User, on_delete=models.CASCADE)
    student_netid = models.IntegerField(null=True, blank=True)
    student_email = models.EmailField(null=True, blank=True)
    file = models.FileField(upload_to="Student/Files/", null=True, blank=True)
    month = models.CharField(max_length=250, null=True, blank=True)
    year = models.CharField(max_length=250, null=True, blank=True)
  1. 关于JSON导出的说明
    用数据库存储完全不影响后续的数据分析需求,只要用Django ORM查询出对应范围的答题记录,调用序列化模块就能直接导出成结构化JSON数据,支持按需筛选字段、过滤范围,比读取零散的磁盘文件灵活度高很多。

内容的提问来源于stack exchange,提问作者Sovit Nayak

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.26 13:54:21