毕业设计做到最后关头,很多同学拿到手的是一个压缩包,标题往往是类似“django在线考试系统-计算机毕业设计源码11387”这样的形式。包里有一堆代码、一个说明文档,运气好还有一份数据库备份。这类题目的关键词已经很明确:后端用django,业务是做在线考试,交付形态是成套毕业设计源码。但真正把它跑起来、讲清楚、答辩不翻车,并没有标题看上去那么轻松。
我当年在这个项目上花的时间不算少,也帮周围同学远程排查过不少类似的问题。这篇文章就当作一份完整的复盘笔记,把从选题拆解、环境搭建、系统设计、核心功能实现,到实际开发中高频踩坑的整个过程都串一遍。无论是准备复现这个项目、打算在此基础上做二次开发,还是纯粹想了解一套在线考试系统内部是如何组织的,这篇都能给你一个比较落地的参考。
1. 先别急着写代码:读懂“11387”这个编号背后的全部信息
1.1 标题里藏着哪些确定信息
一个标题看起来只有寥寥几个词,但信息量其实很大。“django”是技术栈标签,意味着这套系统基于Python开发的Django框架,采用的是MVT架构,自带ORM、Admin后台、认证系统和模板引擎。对于在线考试这种业务场景来说,这些内置能力恰好都能用上。
“在线考试系统”限定了业务领域,围绕的核心功能一般都包括:题库管理、试卷生成、学生在线答题、计时交卷、自动判分、成绩统计。这三个词决定了项目的功能边界,不需要去做什么电商、社交、内容管理,把所有精力放在考试闭环上就好。
“毕业设计”四个字更关键,它说明这不是一个工业级SaaS平台,而是一个“业务完整、界面可用、数据闭环”的教学演示型系统。所以必须有三端角色、有完整的考试流程、有成绩反馈,最好还要有初始数据,否则演示的时候只能现场造数据,会非常狼狈。
至于“源码11387”,更像是源码包平台为了方便检索和管理而加的编号,本身没有业务含义。但有一点要提醒:拿到包之后,第一件事就是确认包内文件与编号对应,不要只看标题就想当然。我见过编号对上了、但内部是另一个项目的包,核对完目录结构才发现。
1.2 源码包到手后,第一件事为什么是核对项目结构
很多同学拿到压缩包,第一反应是解压后直接双击运行,或者打开代码就开始改。我的习惯是先花十分钟做一次“结构审查”,搞清楚包里有什么、缺什么,再动手跑环境。审查的顺序大概是这样的:
- README或部署文档:有没有写环境要求、默认账号、部署步骤。很多源码包会在这里写清楚Python版本和Django版本,这是最重要的信息。
- requirements.txt:依赖列表是否存在,里面有没有标明版本号。如果缺失,后面装依赖时会很痛苦。
- manage.py所在层级:决定你进入项目的路径。有时候源码包把项目套了一层外层文件夹,会导致启动命令找不到manage.py。
- 应用目录划分:有几个app,每个app大概负责什么业务。这一步能帮你快速建立代码地图。
- 数据库文件:有没有db.sqlite3。如果有,大概率自带初始账号和演示数据,跑起来会轻松很多。
- 静态资源与媒体文件:static目录、media目录是否存在。如果缺失,页面样式和上传图片可能会挂。
- 是否有.git目录:如果有,说明有版本历史,遇到改崩的情况还能回滚。
这一步不需要读懂每一行代码,只需要建立“这个项目有哪些组成部分、每个部分大概在哪”的认知。等到后面排查问题时,你能快速定位到对应文件,而不至于在一个几百兆的包里反复翻找。
1.3 文档与代码对不上时的处理策略
源码包里的说明文档经常是通用模板,字段名、模块名和实际代码可能完全对不上。比如文档写“题库管理”,代码里的app可能叫question_bank,视图函数叫question_list。这时候不要慌,也不要急着找售后,先学会“反向追踪法”。
反向追踪的思路是从URL入口开始,顺着代码往底层看。先打开config/urls.py或项目主urls.py,看每个路径对应哪个app、哪个视图函数;然后进到views.py看这个视图渲染哪个模板、操作哪些模型;再打开models.py看清楚数据表结构。四层走完,一个业务模块的真实实现就清楚了。
我遇到过不少这样的情况:说明书里写了A功能,但代码里根本找不到对应视图,反而有一个没写进文档的B功能。这时候你要么接受现实按代码来演示,要么在答辩前自己补齐这个模块。相比之下,相信代码本身通常比相信文档更可靠。不要带着“文档没写=系统没有”的判断去答辩,评委一旦翻代码,局面会很难看。
2. 环境准备与项目启动:从零跑通系统的完整链路
2.1 Python版本与虚拟环境的选择
跑通项目的第一个难关就是环境。Django框架对Python版本有明确要求,如果版本不匹配,启动时会出现“You are using a version of Python that is not supported”之类的报错。遇到这个问题时不要急着改代码,先看一下requirements.txt里的Django版本,再决定安装哪个Python版本。
| Django版本 | 常见兼容Python版本 | 适用场景 |
|---|---|---|
| Django 2.2.x | Python 3.5-3.9 | 老项目,兼容差,不建议新用 |
| Django 3.2.x | Python 3.6-3.9 | 兼容性较好,老毕设常见 |
| Django 4.2.x | Python 3.8-3.12 | 目前主流,性能和安全更好 |
| Django 5.0.x | Python 3.10-3.12 | 较新,适合新项目 |
安装好对应Python版本后,强烈建议用虚拟环境,不要直接把依赖装到全局环境里。虚拟环境的好处是,无论你的电脑上装了其他什么项目,这个项目的依赖都是隔离的,互不干扰。
python -m venv venv # Windows下激活 venv\Scripts\activate # Linux/macOS下激活 source venv/bin/activate激活后执行pip install -r requirements.txt。如果依赖安装中途报错,大概率是某个包只支持特定Python版本,需要手动调整版本号。
2.2 依赖安装与数据库初始化
依赖装完之后,接下来是数据库。这里有一个很多人不知道的技巧:如果源码包里自带了db.sqlite3文件,优先直接使用这个数据库,而不是重新执行migrate。
原因很简单,很多毕业设计源码包里的初始数据是靠数据库备份文件带过来的,而不是通过迁移文件生成的。直接migrate得到的往往是一堆空表,管理员账号、课程、试题全都得手动造,演示效率极低。用自带的db.sqlite3启动,一步就能看到完整数据。
确定数据库方案后,按情况执行对应操作:
# 如果使用自带数据库,先尝试直接启动 python manage.py runserver # 如果没有数据库文件,则先初始化 python manage.py migrate # 如果源码包里有initial_data.json之类的数据文件 python manage.py loaddata initial_data.json # 如果没有初始账号,创建超级管理员 python manage.py createsuperuser注意,如果源码用的是PostgreSQL或MySQL,而你的机器上没装对应数据库,启动时会报驱动错误。这种情况下要么安装对应数据库并修改settings里的连接配置,要么把数据库切换回SQLite。对毕业设计演示来说,SQLite完全够用,不需要追求高并发。
2.3 启动开发服务器与常见启动失败
环境配置完成后,在manage.py所在的目录执行python manage.py runserver,默认监听8000端口,浏览器访问http://127.0.0.1:8000/看到登录页面,就算初步跑通了。
实际开发中,启动失败的原因通常集中在三类:
| 问题现象 | 可能原因 | 解决思路 |
|---|---|---|
| ModuleNotFoundError | 有依赖没装,或版本不兼容 | 对照requirements.txt补装,或查看报错模块名手动安装 |
| ImproperlyConfigured | settings里读取了环境变量但没配置 | 看settings中os.environ相关代码,设置对应环境变量 |
| DatabaseError | 数据库表不存在或数据库未迁移 | 执行migrate或使用自带db文件 |
如果要在局域网内给朋友演示,可以用python manage.py runserver 0.0.0.0:8000,同时记得在settings.py的ALLOWED_HOSTS数组里加上你的局域网IP,否则会返回DisallowedHost错误。
2.4 初始账号与数据验证
系统能启动不代表业务能跑通。我在验证一个毕业设计项目时,一定会走一遍完整的“数据链路测试”:登录管理员账号,进后台创建一门课程和一个教师账号;用教师账号录入几道试题,创建一份试卷;再用学生账号参加考试、交卷、查看成绩。
这个流程走完,基本就能确认系统是可用的。如果README里给了默认账号,优先用默认账号登录;如果没有,就用createsuperuser创建一个超管,然后在后台把用户角色分配好。为了演示方便,我会把所有测试账号的密码统一设置为简单密码,比如123456之类的,省得答辩时记不清。
数据库里已有演示数据时,不要急着清空重来,先保留原样。等到完全理解系统的业务逻辑后,再根据自己需要调整。
3. 系统架构与数据模型:一个在线考试系统该有的骨架
3.1 模块划分与技术选型思路
为什么在线考试系统特别适合用Django来实现?因为它的核心业务几乎就是“数据录入、数据查询、状态流转”,而Django的ORM模型、Admin后台、认证系统、模板引擎恰好覆盖了这些需求。
做这类系统时,按角色划分功能模块是最常见的做法。管理员负责基础数据维护,比如创建课程、管理用户、配置系统参数;教师负责业务数据,比如录入试题、创建试卷、查看成绩;学生负责参与考试,比如查看待考列表、在线答题、查询分数。三个角色的功能边界清晰,页面菜单也容易设计。
这里有一个需要想清楚的设计决策:教师端和管理端到底是做成独立页面,还是直接依赖Django自带的Admin后台。如果源码包用的是Admin后台,那老师和管理员的所有操作都在/admin路径下完成,前台只有学生端页面。这个方案开发量小、能跑,但答辩时会显得业务分离不够明显。如果教师端也有独立页面,则说明系统在“面向用户”的设计上做得更完整。
3.2 核心数据表设计与关系
不管源码包长什么样,在线考试系统的数据模型基本万变不离其宗。理解这套表关系,你就理解了整个系统的核心骨架。
| 表名 | 核心字段 | 主要关联 | 一句话说明 |
|---|---|---|---|
| User | username、password、role | - | 系统用户,扩展自Django用户模型 |
| Course | name、teacher | FK到User | 课程 |
| QuestionBank | course、type、content、answer | FK到Course | 试题库 |
| Exam | title、course、start_time、duration | FK到Course、M2M到Question | 试卷 |
| ExamRecord | student、exam、status、score | FK到User、FK到Exam | 考试记录 |
| AnswerRecord | exam_record、question、selected | FK到ExamRecord | 答题明细 |
User表通常通过AUTH_USER_MODEL扩展,增加role字段区分学生、教师、管理员,而不是建三张完全独立的用户表,这个设计更符合Django的实践。
Exam与Question之间的关系有两种常见实现。一种是“固定试卷”,创建试卷时直接选定题目集合,所有学生考同一套题;另一种是“动态抽题”,试卷只配置课程、题型、数量,学生点击开始考试时才从题库里随机抽取。前者在表结构上体现为Exam与Question的M2M关联表,后者则是在考试记录生成时才创建答题明细。做毕业设计时,我更推荐在文档里把抽题逻辑讲清楚,这是答辩中很容易被追问的点。
3.3 Django MVT的目录组织
一个结构清晰的在线考试系统项目,目录组织通常长这样:
project_root/ manage.py config/ # 项目配置 settings.py urls.py asgi.py wsgi.py apps/ users/ # 用户与角色管理 courses/ # 课程管理 question_bank/ # 题库管理 exam/ # 考试与答题 analysis/ # 成绩统计 static/ # 全局静态资源 templates/ # 全局模板 media/ # 用户上传文件 db.sqlite3每个app内部都是Django标准结构:models.py定义数据表,views.py写业务逻辑,urls.py配置路由,admin.py注册后台管理,migrations/里是数据库迁移文件。这样的好处是业务解耦,后期如果给题库增加了新功能,不需要去动考试模块的代码,直接在question_bank里扩展就行。
如果你的源码包把所有业务都写在了一个app里,比如只有一个main或core,也不用紧张。它一样能跑,只是目录组织稍显混乱。二次开发前,可以先按业务模块把代码梳理清楚,再动手改。我在实际操作中,遇到过单个app几千行的views.py,那时最有效的做法是先给每个视图函数标注归属模块,再逐步拆分。
3.4 自定义用户模型与权限体系
权限设计是这类系统里最容易“看似简单、实则麻烦”的部分。很多源码包会直接给User模型加一个role字段,比如1表示管理员、2表示教师、3表示学生。这个做法的优点是简单直观,缺点是权限判断的代码会散落各处,一旦要调整权限就得全局搜索。
还有一种做法是使用Django原生的Group和Permission,管理员创建“教师组”“学生组”,通过组来控制权限。这样在Admin后台里就能直观看到用户有哪些权限,答辩时也更经得起问。
无论用哪种方式,视图层的权限控制都建议用装饰器统一处理,而不是在每个视图里写重复的if判断。下面是一个简化示例:
def teacher_required(view_func): @wraps(view_func) def wrapper(request, *args, **kwargs): if not request.user.is_authenticated: return redirect("login") if request.user.role != 2: return HttpResponseForbidden("仅教师可访问") return view_func(request, *args, **kwargs) return wrapper模板层也要配合处理,菜单和按钮需要在页面里根据用户角色显示或隐藏,直接在模板中用{% if request.user.role == 2 %}就能实现。这两个层面都做完整,权限体系才算闭环。
4. 核心功能模块的实现逻辑:从登录到出分
4.1 登录与会话控制
在线考试系统的登录入口一般就一个,但登录成功后的路由要按角色分流。Django自带authenticate()和login()函数,处理密码校验和会话创建,这部分不需要额外造轮子:
def login_view(request): if request.method == "POST": username = request.POST.get("username") password = request.POST.get("password") user = authenticate(request, username=username, password=password) if user is not None: login(request, user) if user.role == 1: return redirect("admin_dashboard") elif user.role == 2: return redirect("teacher_dashboard") else: return redirect("student_dashboard") return render(request, "login.html", {"error": "用户名或密码错误"}) return render(request, "login.html")关于考试状态,很多人会习惯放Session里。但要注意,Session在浏览器刷新、关闭或切换设备后可能丢失。正式一点的做法是在数据库里维护ExamRecord状态,学生退出后重新进入时,根据考试记录和当前时间判断是继续考试、还是标记缺考、还是已经交卷。这个设计在答辩中也可以作为亮点讲。
4.2 题库管理与试题导入
教师端的题库管理是系统数据的基础,没有题库,后面的组卷和判分都是空中楼阁。基础功能是增删改查,但真正体现工作量的是批量导入。
批量导入推荐用xlsx而不是csv。很多从Excel另存的csv文件是GBK编码,用pandas或openpyxl读取时容易出现乱码。直接用openpyxl读xlsx,编码问题会少很多。导入流程建议设计为“先解析、再校验、后入库”三步骤,千万不要边解析边入库,否则一行坏数据会导致整个文件导入失败,排查起来非常头疼。
校验逻辑至少包含:必填字段是否为空、选项格式是否合法、答案是否在选项范围内、题目类型是否在枚举范围内。校验失败的行要单独记录失败原因,入库完成后返回“成功N条,失败M条”的结果列表。这样用户体验好,出问题也好定位。
试题的题型至少要支持单选、多选、判断,有条件可以加填空题。选择题在数据库里通常用选项文本存储,多个选项之间用指定分隔符隔开,判分时再按分隔符拆分。判断题答案一般用对/错或T/F存储。这些看起来是很小的设计决策,但会影响判分逻辑的复杂度。
4.3 在线考试:抽题、计时、自动交卷
这是整个系统的核心模块,也是最容易出bug的地方。完整的考试流程包括:学生点击“开始考试”前的资格校验、抽题、进入答题页、逐题保存答案、倒计时、交卷、自动判分。
资格校验要判断三点:当前时间是否在考试时间窗口内、学生是否已经考过、试卷是否有效。前两点很容易理解,第三点容易被忽略——如果教师创建了试卷但没设置考试时间,学生端就不该看到这张试卷。
抽题的逻辑根据组卷方式不同而不同。固定试卷直接读取Exam与Question的关联记录;动态抽题则是在学生点击开始考试的那一刻,从题库中随机选择。动态抽题最简单的实现是:
questions = Question.objects.filter( course=exam.course, question_type=type ).order_by("?")[:count]order_by("?")会在数据库层做随机排序,对于题库量几百条的毕业设计场景完全够用。如果题库量达到上万条,全表随机排序会比较慢,可以先取Random值采样ID再查数据,但作为毕业设计,讲到“我用order_by查询随机抽样”就已经足够了。
答题过程中,学生每选一道题就通过AJAX把答案暂存到AnswerRecord。这个设计非常关键,否则学生最后一键交卷时,如果网络卡顿或页面误刷新,所有已选答案都会丢失。暂存答案的视图大致长这样:
def save_answer(request): if request.method == "POST": record_id = request.POST.get("record_id") question_id = request.POST.get("question_id") selected = request.POST.get("selected") AnswerRecord.objects.update_or_create( exam_record_id=record_id, question_id=question_id, defaults={"selected": selected} ) return JsonResponse({"code": 0, "msg": "ok"}) return JsonResponse({"code": 1, "msg": "method error"})倒计时的实现也容易踩坑。如果前端用setInterval自己计时,页面一刷新倒计时就归零重来,有心的学生可以做手脚。更好的做法是后端下发一个“考试截止时间戳”,前端根据截止时间戳倒计时;同时后端在每次保存答案时检查当前时间是否已超过截止时间,超过则强制标记交卷。这样即使前端被篡改,后端依然有兜底。
4.4 客观题自动判分与成绩统计
自动判分是客观题系统的标配功能。判分的核心逻辑其实不复杂:遍历某个考试记录下的所有答题明细,对比学生所选答案与标准答案,每题判断正确与否,最后汇总得分。
@transaction.atomic def submit_exam(request): record = ExamRecord.objects.select_for_update().get(id=request.POST.get("record_id")) if record.status == "submitted": return JsonResponse({"code": 1, "msg": "已交卷,请勿重复操作"}) answer_records = AnswerRecord.objects.filter(exam_record=record) total_score = 0 for answer in answer_records: is_correct = compare_answer(answer.question, answer.selected) answer.is_correct = is_correct answer.save(update_fields=["is_correct"]) if is_correct: total_score += answer.question.score record.score = total_score record.status = "submitted" record.save(update_fields=["score", "status", "submit_time"]) return JsonResponse({"code": 0, "score": total_score})判分要注意多选题的策略:是“全对才得分”还是“按比例得分”。毕业设计一般选前者,逻辑简单,演示时也容易解释。判断题则要把“对/错”与“T/F”这类格式统一归一化后再比较,否则会因格式不一致导致误判。
成绩统计一般包括平均分、及格率、最高分、最低分、分数段分布,用Django的聚合函数可以很方便地算出来:
from django.db.models import Avg, Max, Min, Count stats = ExamRecord.objects.filter(exam=exam).aggregate( avg_score=Avg("score"), max_score=Max("score"), min_score=Min("score"), total=Count("id") )及格率的计算需要在Python里统计score >= pass_score的记录数,然后除以总数。这一部分建议用图表展示,答辩时一个曲线图比念表格数据有说服力得多。
5. 踩坑记录:开发中最容易翻车的五个环节
5.1 时区问题:考试时间总是差8小时
我第一次测试在线考试功能时,教师后台设置的考试开始时间是14:00,学生页面上显示却是06:00。当时第一反应是前端模板格式化出了问题,排查了半天才发现是Django时区设置没有配好。
Django默认USE_TZ=True,数据库里存的是UTC时间,模板渲染时会把UTC时间按当前TIME_ZONE转换。如果TIME_ZONE是UTC而你在东八区,页面显示的自然就是“少了8小时”。解决方法是把settings.py里的TIME_ZONE改成“Asia/Shanghai”,同时对于需要精确判断的时间,在后端统一使用datetime.now()而不是timezone.now(),避免混合使用导致偏移。
这个坑在答辩前必须验证一遍。最佳测试方法是把试卷的开始时间设成当前时间加5分钟,然后走完整场考试,亲眼看到“待开始-考试中-已交卷”三个状态切换正确。
5.2 并发与重复提交:双击交卷导致成绩异常
有一次做压力测试,发现某位学生交卷后,数据库里出现了两条ExamRecord记录,成绩还不一样。查日志后发现,问题是交卷按钮用AJAX提交,但前端没有在点击后立刻禁用按钮。学生双击了按钮,两个请求几乎同时间到达后端,两个事务都通过了状态检查,各自生成了成绩。
这个问题的修复要从两头下手。前端在点击后立刻把按钮设为disabled,并加上“正在提交”的文案,防止用户重复点击。后端在写成绩之前,用事务加上行级锁,以考试记录为粒度做状态机判断。上面第4节里给出的select_for_update() + status判断就是这个思路。很多毕业设计项目会忽略这种并发场景,但评委如果问到“如何防止重复交卷”,你能拿出锁和状态机这套方案,会明显加分。
5.3 试题的换行与富文本问题
题库管理里输入的题干或选项如果有换行,前端展示时往往挤成一坨。原因是HTML里默认会把连续空白字符压缩成一个空格,textarea里的换行符在页面上不会按原样显示。
如果题目只是纯文本,最简单的修复是在答题页给题干的容器加上CSS样式white-space: pre-wrap,让换行符能被正常渲染。如果需要更好的展示效果,可以在录入时支持简单的Markdown或者富文本,渲染时通过模板过滤器允许HTML输出。但要注意,允许输出HTML就意味着要控制输入来源,否则有可能把脚本代码也渲染出去。凡是在模板里用了safe过滤器的地方,都要确认数据来源是可信的。
5.4 静态文件404:settings配置里的隐藏雷
系统登录页有样式,但验证码图片和用户上传的图片全部404。排查到最后发现,项目里配置了STATIC_URL,但没有配置媒体文件的处理路由,导致浏览器请求/media/路径时全部落到404。
开发阶段Django会自动处理static目录,但media目录需要在项目的urls.py里手动追加:
from django.conf import settings from django.conf.urls.static import static urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)不要小看这个配置,因为很多源码包提交时的环境是作者本机,media配置可能依赖本地路径,换一台机器就失效。答辩前把static和media都验证一遍,尤其是上传图片相关的功能,最好提前跑一轮。
5.5 批量导入Excel试题时的编码与格式校验
批量导入是教师端很常用的功能,也是问题高发区。最常见的翻车现场是:导入xlsx时中文乱码、某一行少了一个字段导致整批失败、判断题答案写了“√”和“×”程序识别不了。
乱码的根因是没搞清楚文件的编码格式,xlsx用openpyxl读取时基本没有编码问题,但csv文件在不同环境下可能是GBK或UTF-8,读取前最好先做编码嗅探。
格式校验方面,推荐的做法是“先全量解析,再逐行校验,最后批量入库”。解析阶段把Excel每一行转成字典对象,校验阶段检查必填字段、枚举值、选项数量,入库阶段再统一写入数据库。用这种三段式处理,即使1000条数据里有100条不合格,前面900条也不会因此丢失。顺便说一句,导入功能提交前一定要用特别脏的数据测试,比如带空格的邮箱、全角字符的标点、混合的换行符,这些都是教师录入时常见的手误。
6. 答辩准备与项目扩展:让毕业设计更值钱
6.1 演示系统的演示路线设计
很多同学答辩时习惯把所有功能点都点一遍,结果时间不够、重点不突出。更好的做法是设计一条完整的业务主线,每个步骤之间用“上一步的产出是下一步的输入”来衔接,评委顺着这条线走,很容易理解系统价值。
我建议按这个路线演示:
- 管理员登录后台,创建一门课程,分配一位教师账号
- 教师登录,进入题库管理,手动录入3到5道题,最好覆盖单选、多选、判断三种题型
- 教师创建一张试卷,设置考试时间、时长、总分,并把刚录入的题目加入试卷
- 切换学生账号登录,看到待考列表,进入考试,完成作答,提交试卷
- 回到教师端,查看成绩统计,展示平均分、及格率、分数段
每一步都要能说出对应的数据表是什么、关键代码写在哪个文件里。演示前先在测试环境完整走两遍,确认不会在中途出现意外。
6.2 高频技术问题与回答思路
答辩时评委不会只看功能,还会问实现思路和方案取舍。以下几个问题命中率极高,可以提前准备:
- 为什么选Django不选Flask或其他框架?最直接的答案是Django自带ORM、Admin后台、认证和模板引擎,能快速构建业务闭环,适合这类数据密集型系统。
- 如何防止考生作弊?可以回答限时考试、随机抽题、切屏记录、IP记录。如实说明哪部分做了、哪部分没做,比夸夸其谈更可信。
- 如果一千人同时考试,系统会遇到什么问题?这个问题考的是对瓶颈的认知。可以回答SQLite在高并发写入上有限制,换成MySQL或PostgreSQL,配合Redis做缓存后能缓解;同时指出当前表结构是否需要加索引等。
- 题目是怎么随机抽取的?如果是order_by("?"),要能说出它的原理和局限,以及更大数据量下的替代方案。
准备的时候不要背答案,要把每个问题放到自己的代码里去验证一遍,这样被追问到细节时才能接上话。
6.3 值得做的三个扩展方向
如果时间和精力允许,给毕业设计增加一点“增量价值”会让整个项目看起来更有深度。
第一个扩展方向是移动端适配。很多在线考试系统在电脑上显示正常,手机上一打开就错位。至少在答题页适配手机屏幕,支持学生在手机端参加考试,这个实用性很强。
第二个扩展方向是接入Redis做缓存和分布式会话。考试成绩统计和热门课程数据可以用缓存加速,Session存储从数据库迁移到Redis后,也能为并发场景打好底子。答辩时提到“我引入了Redis做缓存”,评委通常会有兴趣。
第三个扩展方向是主观题人工评阅。客观题全自动判分没有难度,加上主观题后,教师可以在后台对主观题打分,学生端能看到阅卷进度和分项得分。这个扩展会让系统从“客观题刷题工具”变成一个更完整的考试平台。
我自己在做扩展时有个习惯:动手前先把数据流图画一遍,特别是新增的功能会涉及哪些现有表、会不会破坏原有闭环,想清楚再写代码。如果让我重新做一遍这个django在线考试系统,我会把更多时间花在数据模型的完整性和异常处理上,而不是反复调页面样式。功能闭环扎实,答辩时的底气就足了一半。