Django少儿英语教学平台开发全攻略:从架构设计到毕业答辩

发布时间:2026/10/1 4:09:13
Django少儿英语教学平台开发全攻略:从架构设计到毕业答辩 每年毕设季“Django少儿英语教学平台”这个题目总会出现在不少同学的选题列表里。用Python的Django框架做Web开发把少儿英语学习的课程、练习、单词闯关、作业打卡和成绩统计做成一条完整业务链最后交付源码、精品论文和答辩PPT这种一条龙的课程设计/毕业设计项目确实是混迹各类技术社区多年的熟面孔。但大部分交上来的版本都死在了同一个地方——功能能做出来却讲不清楚“为什么这么设计”论文里全是截图堆砌答辩时被评委一句“这个表为什么要这样建”就噎住了。Django少儿英语教学平台这类项目本质上是“内容管理系统在线训练系统轻量互动工具”的组合非常适合用来覆盖Django的核心能力模型层ORM、模板引擎、Admin后台、用户认证、表单校验、信号与中间件。这篇文章就围绕我从零到一完成这个项目的整个过程来写从框架选型、系统架构、核心模块实现到论文编排、答辩PPT设计、源码整理规范以及我在实际开发中踩过的一堆坑一次性讲透。适合正在做课程设计、毕业设计或者想用一个完整项目巩固Django技能的Python开发者参考。1. 为什么选Django做这个少儿英语教学平台而不是Flask或Spring Boot1.1 选型逻辑Django天生匹配“教学管理型”项目网上关于Django和Flask的争执一直没停过我的态度很明确如果是个人博客、小型API服务Flask的灵活性确实舒服但如果目标是一个需要覆盖多角色权限、内容管理、班级学习数据的教学平台Django的“全家桶”特性会省掉大量重复造轮子时间。先看这个项目要解决什么问题。少儿英语教学平台有四种典型角色管理员要维护课程和教师账号老师要发布课程内容、布置作业并查看班级正确率学生要上课、答题、提交作业家长要看到孩子的学习进度和成绩曲线。这种“多角色内容管理数据统计”的结构恰好对应Django的自带能力django.contrib.auth提供用户体系django.contrib.admin直接生成管理后台ORM能快速建立外键关系并做聚合统计Django模板引擎则能直接渲染服务端页面。它给我们提供了一个经过验证的、稳定可扩展的骨架而不是要求我们从路由、ORM、Session、Admin无到有拼出一套轮子。对比Spring Boot也不是不能做但Java体系太重光是环境配置和依赖管理就能劝退一批以Python为第一语言的同学。再说Django社区里django-unfold这类Admin美化方案也很多把后台界面换成现代化主题以后演示效果完全不输那些重型管理系统这也是我当时坚持用Django的一个重要原因——“少儿英语”项目的受众和家长角色本身就很吃“后台界面看起来专业”这一套。1.2 平台的核心痛点拆解别把项目做成“课程展示页”我见过很多同类毕设一上来就做了一堆花花绿绿的页面课程列表、教师介绍、新闻公告做得像企业官网但评委最关心的学习闭环一个都没落地。这个项目的核心痛点我拆成三个递进层次第一层是“内容怎么管”。老师不可能每次发布课程都让人改代码所以必须有一个可视化后台能够录入课程标题、课时介绍、题目选项并且把课程和课时、课时的练习题关联起来。第二层是“学习怎么练”。学生做完题目后系统要能自动判分记录每次答题的结果否则“在线学习”就是空中楼阁。第三层是“数据怎么看”。每个人都想知道孩子的真实学习情况所以家长端和学习报告需要把同一份数据以不同维度呈现出来。这三个层次直接决定了项目的功能边界和数据库设计方向。我当时给这个平台定的定位是轻量、可靠、闭环清晰。不要做视频直播不要做AI口语评测先把“课程—学习—练习—测评—反馈”这条主线走通让每一张数据库表都有明确的存在理由。这个判断在后面写论文和准备答辩时帮了大忙因为整个系统的每个模块都能用业务逻辑解释清楚而不是“为了完成题目硬凑功能”。2. Django少儿英语教学平台的整体架构与模块拆解2.1 MVT架构在项目里的真实落地方式Django的MVTModel-View-Template是人人都能背的概念但很多初学者并没有真正理解它在一次请求里是怎么协作的。拿这个平台里的一个具体场景举例学生在浏览器里访问/lesson/5/detail/请求首先被urls.py里的URLconf匹配到LessonDetailView这个视图函数视图通过Lesson.objects.get(pk5)从数据库取到课时对象同时把该课时关联的题目、课件、视频地址传给lesson_detail.html模板模板最终渲染成完整的HTML返回浏览器。每个模块都沿用了这套统一流程models.py定义数据结构views.py处理业务逻辑urls.py做路由映射templates/负责页面展示。这样做的好处是项目结构一目了然新增一个功能时只需要“加Model—加View—加URL—加模板”四步。我在项目里还统一使用了base.html模板继承导航栏、页头、底部信息都在父模板里维护这样课程列表页、答题页、成绩页共用一套视觉框架也避免了多处修改样式的麻烦。2.2 四类用户角色与六大功能模块设计模块设计不能拍脑袋得先从角色行为推导。以下是我实际使用的角色权限矩阵角色核心操作权限来源管理员创建教师账号、管理课程分类、查看全站数据is_staffis_superuser教师发布课程/课时、布置作业、查看班级统计自定义role字段 老师分组学生学习课时、在线答题、提交作业、查看个人成绩登录用户 role字段家长绑定孩子、查看孩子学习报告登录用户 孩子外键关联对应的功能模块分为六个课程管理中心负责课程和课时的增删改查在线课堂模块负责学习内容的展示和课件管理题库练习模块负责题目维护、答题判分、错题记录作业打卡模块负责作业发布与提交状态跟踪成绩统计模块按学生、课时、课程三个维度聚合正确率家长监管模块让家长通过绑定关系查看孩子学习数据。模块之间不是孤立的关系可以这样描述教师发布课程课程中心→ 课程关联多个课时在线课堂→ 课时关联练习题题库练习→ 学生答题产生答题记录练习数据→ 答题记录汇聚成成绩统计成绩分析→ 家长查看对应学生的统计数据家长端。答辩时把这条链路画清楚整个系统的价值自然就出来了。2.3 核心数据模型设计与业务边界划分数据模型是这个项目的灵魂我当时在建模时花的时间比写视图多得多。用户模型不建议单独用OneToOneField去扩展Profile更推荐直接继承AbstractUser在项目初始化阶段就把AUTH_USER_MODEL指定好否则后续做关联查询会非常别扭。核心的课程与课时模型大概是这样from django.db import models class Course(models.Model): LEVEL_CHOICES [(K1, 启蒙级), (K2, 初级), (K3, 中级)] title models.CharField(max_length200, verbose_name课程标题) description models.TextField(blankTrue, verbose_name课程简介) cover models.ImageField(upload_tocourse_covers/, blankTrue, nullTrue, verbose_name封面图) level models.CharField(max_length20, choicesLEVEL_CHOICES, defaultK1, verbose_name课程级别) created_at models.DateTimeField(auto_now_addTrue, verbose_name创建时间) class Meta: ordering [-created_at] verbose_name 课程 verbose_name_plural 课程 def __str__(self): return self.title class Lesson(models.Model): course models.ForeignKey(Course, on_deletemodels.CASCADE, related_namelessons, verbose_name所属课程) title models.CharField(max_length200, verbose_name课时标题) content models.TextField(verbose_name学习内容) video_url models.URLField(blankTrue, verbose_name视频地址) order models.PositiveIntegerField(default1, verbose_name课时序号) class Meta: ordering [course_id, order] unique_together (course, order) def __str__(self): return f{self.course.title} - {self.title}这里有几个设计细节容易被忽略on_deletemodels.CASCADE表示删除课程时会级联删除所有课时这在业务上是合理的但需要你在论文中明确说明这个行为related_namelessons让反向查询course.lessons.all()变得顺手unique_together保证同一个课程下课时序号不重复从数据层面避免了排序混乱。题目和答题记录是练习模块的核心答题记录必须同时关联学生和题目并且冗余保存is_correct字段因为判分结果在生成那一刻就确定了后续不需要反复重新比对正确答案。class Question(models.Model): lesson models.ForeignKey(Lesson, on_deletemodels.CASCADE, related_namequestions) stem models.TextField(verbose_name题干) option_a models.CharField(max_length200, default) option_b models.CharField(max_length200, default) option_c models.CharField(max_length200, blankTrue, default) option_d models.CharField(max_length200, blankTrue, default) answer models.CharField(max_length1, choices[(A, A), (B, B), (C, C), (D, D)], verbose_name正确答案) class AnswerRecord(models.Model): student models.ForeignKey(settings.AUTH_USER_MODEL, on_deletemodels.CASCADE, related_nameanswer_records) question models.ForeignKey(Question, on_deletemodels.CASCADE) selected models.CharField(max_length1, verbose_name学生选项) is_correct models.BooleanField(defaultFalse, verbose_name是否正确) answered_at models.DateTimeField(auto_now_addTrue)整个模型设计遵循一个原则每张表都对应一个明确的业务动作。Course对应“开设课程”Lesson对应“安排课时”Question对应“题目录入”AnswerRecord对应“学生答题”Submission对应“作业提交”。答辩时如果评委问“为什么需要这张表”你都能答出它承载的具体业务场景这就叫业务边界清晰。3. 核心功能实现全流程从空目录到能演示3.1 环境准备Python、venv和Django版本别乱装很多新手拿到项目第一步就卡在环境上这里把最稳的做法写一遍。推荐使用Python 3.10或3.11版本搭配Django 4.2 LTS或5.0。Django对Python版本有严格要求装错组合在启动时就会报错。python --version python -m venv venv # Windows: venv\Scripts\activate source venv/bin/activate pip install django4.2 pillow用虚拟环境是必须的不然Django依赖会跟系统其他项目的包冲突做一个项目污染一次系统Python后面维护就是噩梦。开发工具方面我习惯用VS Code装好Python插件后在命令面板里执行“Python: Select Interpreter”选到venv里的解释器这样终端运行和调试都能直接识别Django环境。这里额外提醒一句pillow必须装只要你的课程模型里有ImageField迁移时少它就会报AppRegistryNotReady一类莫名其妙的错。3.2 创建项目与App划分新建工程和业务模块的时候App的边界划分直接决定后期维护体验。我的建议是不要把整个平台塞进一个App里而是按业务模块拆分django-admin startproject kid_english . python manage.py startapp users python manage.py startapp courses python manage.py startapp exercises python manage.py startapp notices python manage.py startapp stats每个App职责单一users管登录注册和家长绑定courses管课程与课时exercises管题库和答题记录notices管消息通知stats管成绩统计与报表。这样做的直接收益是后面写论文时每个功能模块对应一个App架构图非常好画答辩讲起来也顺畅。设置文件里几个关键点必须在一开始就配置好。AUTH_USER_MODEL users.User要在第一次migrate之前写进settings.py否则后面想换自定义用户模型会遇到一堆外键约束问题。还要顺手改一下LANGUAGE_CODE zh-hans和TIME_ZONE Asia/Shanghai让Admin后台直接显示中文、时间记录用本地时区这些细节会直接出现在论文截图里一眼就能看出项目是否细心。3.3 用户认证、角色权限与Cookie/Token的处理思路认证这块直接使用Django内置的django.contrib.auth登录视图用authenticate和login配合login_required装饰器做页面级访问控制。角色区分上我在User模型里增加了一个role字段用INTEGER_CHOICES区分教师和学生加上Django自带的is_staff标识后台管理员。教师发布课程页面对学生不可见用的就是这类判断。class User(AbstractUser): ROLE_CHOICES [(1, teacher), (2, student)] role models.IntegerField(choicesROLE_CHOICES, default2) nickname models.CharField(max_length50, blankTrue)家长绑定孩子用ForeignKey指向同一个User表实现家长账号下挂一个child models.OneToOneField(settings.AUTH_USER_MODEL, on_deletemodels.CASCADE, related_nameparent)一对一关系确保一个孩子只能被一个家长绑定。这样家长登录后通过request.user.child拿到孩子对象再查询答案记录即可逻辑非常直观。关于Cookie和Token的问题我在项目里的处理是分场景的。传统页面登录走Django的Session机制浏览器自动维护sessionid这个Cookie不需要额外写Token。但如果平台本身提供了API接口比如给小程序端或App端用可以自己实现一个简单的Token模型用户登录成功后生成随机字符串存入Token表客户端后续请求在HTTP头里带Authorization: Token xxx视图里解析这个头确定当前用户。面试或答辩被问到Cookie/Session/Token区别时能说出“Cookie是浏览器的存储机制Session是服务端状态存储Token是无状态认证凭证”这个层次就已经比大部分同学强了。3.4 课程管理、练习判分与成绩统计的实际代码老师录入课程和题目最省事的方案是定制Django Admin。默认Admin已经能用但要做出“精品”效果还是要重写一下显示字段。例如课程后台设置list_display [title, level, created_at]、list_filter [level]、search_fields [title]课时通过TabularInline内嵌在课程编辑页这样老师在一个页面里就能把课程和课时全部维护完不要让学生去碰后台。在线答题是核心交易型场景。一个典型的答题提交视图长这样login_required def submit_answer(request, question_id): question get_object_or_404(Question, pkquestion_id) if request.method POST: selected request.POST.get(answer, ) is_correct (selected.upper() question.answer) AnswerRecord.objects.create( studentrequest.user, questionquestion, selectedselected, is_correctis_correct ) return JsonResponse({correct: is_correct, right_answer: question.answer}) return JsonResponse({error: invalid request}, status400)判分逻辑放在服务端而不是前端是因为前端校验可以随便改源码绕过服务端判分才是可信的。成绩统计我推荐用ORM的聚合查询而不是把记录全查出来再循环计算。比如要统计某个学生在某门课程下每一课时的答题正确率from django.db.models import Count, Q stats AnswerRecord.objects.filter( studentrequest.user, question__lesson__course__idcourse_id ).values(question__lesson__title).annotate( totalCount(id), correctCount(id, filterQ(is_correctTrue)) )数据库端完成分组计数性能比Python内存循环高一个量级。统计结果可以直接传给模板渲染成表格也可以传给ECharts前端图表库展示。这里也是在论文“系统实现”章节中非常有说服力的代码示例。3.5 老师发布新作业后数据怎么实时推到学生端“后台有数据、前端自动推送”是这个项目里最能展示个人能力的技术点之一。我的做法是使用channels库实现WebSocket让老师在后台发布新课程或作业时学生端页面能立即收到通知不需要刷新。标准实现有三块。第一块在settings.py里把channels加入INSTALLED_APPS并配置ASGI应用第二块写consumers.py定义WebSocket连接逻辑第三块在前端页面里建立WebSocket连接监听消息。一个简化版的消息消费者# consumers.py import json from channels.generic.websocket import AsyncWebsocketConsumer class NoticeConsumer(AsyncWebsocketConsumer): async def connect(self): self.group_name classroom await self.channel_layer.group_add(self.group_name, self.channel_name) await self.accept() async def disconnect(self, close_code): await self.channel_layer.group_discard(self.group_name, self.channel_name) async def receive(self, text_data): data json.loads(text_data) await self.channel_layer.group_send( self.group_name, {type: notice.message, message: data[message]} ) async def notice_message(self, event): await self.send(text_datajson.dumps({message: event[message]}))老师提交新作业的视图里通过channel_layer.group_send()向classroom组推送消息所有连接了这个组的学生端就会实时收到提示。这里提醒一句Channels默认用内存版Channel Layer只适合开发演示一旦重启服务进程消息就丢了真要上生产还得配Redis。毕设项目演示够用但论文里建议把Redis方案也写一笔显得你考虑到了生产环境问题。如果时间实在不够备选方案是前端定时轮询接口每10秒请求一次/api/notices/latest/也能实现“接近实时”的效果。轮询实现简单、部署稳定但技术含量不如WebSocket答辩时容易被追问“为什么用轮询而不是WebSocket”所以我的意见是能做WebSocket就做。4. 精品论文、答辩PPT和源码交付的“一条龙”经验4.1 论文结构怎么组织评委最看重哪几页这个项目既然带了“精品论文”作为交付物论文本身就不能是代码的流水账。我验证过最稳的章节结构如下章节核心内容建议篇幅第1章 绪论项目背景、国内外研究现状、可行性分析4~6页第2章 需求分析可行性分析、用户角色、功能需求、非功能需求6~8页第3章 系统设计总体架构、功能模块分解、数据库设计、E-R图10~12页第4章 系统实现核心功能实现流程、关键代码、界面截图12~16页第5章 系统测试测试环境、功能测试用例、测试结果分析6~8页第6章 总结与展望项目成果总结、不足与展望2~3页评委最看重的其实是“数据模型设计”和“测试部分”。很多人论文里没有画出完整的E-R图或者测试只用一句“系统运行正常”带过分数很难上去。数据库设计必须交代清楚每张表的主键、外键和关联关系我当时画E-R图用的是draw.io表结构和关系一目了然。测试部分不要求自动化测试覆盖率但至少要有功能测试用例表写明测试编号、测试内容、预期结果、实际结果、是否通过。论文中出现的截图也值得花时间打磨。Admin后台用django-unfold美化一下界面页面浏览器的URL地址栏不要暴露localhost:8000这种开发地址统一用一个虚拟域名截图之前把浏览器窗口调成统一尺寸这些细节拼起来就是“精品”和“普通”的差距。4.2 答辩PPT的演示顺序与高频提问应对PPT不要超过12页顺序建议是研究背景与意义→需求分析→系统架构设计→功能模块展示→核心代码讲解→测试与总结。演示环节最重要的是真实感打开的页面、数据库记录、操作流程都要提前演练三遍尤其是WebSocket实时推送这种动态效果一定要保证现场能复现否则不要放进演示里。评委最爱问的问题其实就那么几个提前准备好就不会卡壳提问应答思路为什么选择Django对比Flask轻量但不含Admin/Auth/ORM组件对比Spring Boot环境重Django全家桶匹配教学管理场景ORM和原生SQL有什么区别ORM用面向对象方式操作数据库自动处理SQL注入问题通过connection.queries可查看实际SQL课程删除了课时怎么办on_deleteCASCADE级联删除如需保留数据可改为SET_NULL并在字段上设置nullTrue日志和异常怎么处理的使用日志模块记录错误视图层通过捕获DoesNotExist等异常返回404页面有多少用户量性能怎么保证数据库索引、分页、select_related减少查询静态资源交给NginxWebSocket用的Redis Channel Layer注意回答的逻辑是“我知道问题存在并且我有解决方案”而不是“我没考虑过”。4.3 源码交付别踩的坑从README到requirements源码交付不是把代码发过去就完事。一个合格的交付包必须有清晰的目录结构、可复现的运行说明、完整的环境依赖列表。以下几件事我一直强调第一requirements.txt必须和实际环境一致。在虚拟环境里执行pip freeze requirements.txt不要在全局环境里生成否则会带进一堆无关包。第二db.sqlite3、media/里的用户上传文件、settings.py里的SECRET_KEY不应该提交到源码包换成提供初始数据的data.json或seed.py脚本别人拿到源码后跑一次python manage.py loaddata data.json就能看到演示数据。第三README要包含从创建虚拟环境、安装依赖、执行迁移、创建超级用户、启动开发服务器到初始化演示数据的完整命令序列我见过太多项目源码本身写得没问题但因为缺启动说明被判定为“无法运行”。源码本身也值得做一次代码评审。视图里不要堆几百行业务逻辑复杂过程拆到services.py或模型方法里URL命名用语义化的name参数模板里不要写SQL查询每个自定义的Python文件顶部写上模块级docstring。这些代码规范在答辩时虽然不一定会被直接问到但积累下来的整体代码质量对任何一个有经验的评委来说都是一眼能看出来的差别。5. 实战中踩过的坑与排查技巧实录5.1 ORM查询最容易翻车的地方惰性、get、删除与外键热搜词里有一条是“django执行查询-删除对象”这确实是Django开发里最容易被忽视的角落。第一个坑是QuerySet的惰性求值。lessons Lesson.objects.filter(coursecourse)这一行并不会真正执行SQL只有当代码迭代这个QuerySet、调用list()或者取切片时才会访问数据库。这个特性有两面性好处是你可以把多个过滤条件链式叠加而不产生查询坏处是如果你把它传给模板然后在模板里循环了两次就会执行两次SQL性能问题就这么来的。第二个坑是get()方法。Lesson.objects.get(id5)在记录不存在时抛DoesNotExist存在多条记录时抛MultipleObjectsReturned这两个异常都必须处理。我的一贯做法是用get_object_or_404()代替裸get()在视图层它能自动返回404页面在业务代码里也可以用try...except包住避免向用户暴露500错误。第三个坑是关于delete()的Django的delete()是批量删除操作它不会调用每个对象重写的save()方法只执行SQL层的DELETE。另外ForeignKey的on_delete行为不是Django本身模拟的而是由它在创建外键时生成的数据库约束来保证。这意味着如果你用raw SQL绕过ORM去操作数据库on_deleteCASCADE依然生效但Django信号却不会触发。所以在设计数据模型时一定要明确这个外键的级联行为是否符合业务要求。避免N1查询则是性能优化的基本功。当获取课程列表后模板里又要循环访问每门课的课时数时默认会产生“1N”条SQL。此时在视图查询里加上Course.objects.select_related(xxx).prefetch_related(lessons)就能通过JOIN或第二条SQL把关联数据一并查出来。我在实际测试中课程列表页从200多条SQL减少到2条页面响应时间肉眼可见地下降。5.2 CSRF、Cookie与Token表单和Fetch请求的登录问题热词里的“django cookie设置token”对应的坑我在项目里几乎都踩了一遍。普通表单提交时模板里放了{% csrf_token %}标签Django的CSRF中间件能正常验证但当你用原生JS的fetch()发送POST请求时如果不带CSRF Token请求会被403拦截。解决方法是先从Cookie里读出csrftoken然后在请求头里加上X-CSRFTokenconst csrftoken document.cookie.split(; ) .filter(row row.startsWith(csrftoken)) .map(row row.split()[1])[0]; fetch(/api/submit_answer/, { method: POST, headers: { Content-Type: application/json, X-CSRFToken: csrftoken }, body: JSON.stringify({ question_id: 5, answer: B }) });这就解决了“Cookie里明明有Token但接口还是403”的问题。如果你自己实现了Token认证建议统一使用Authorization请求头传递让Token和Session认证的代码路径互不干扰。不要把Token放进URL参数里否则Token会出现在浏览器历史、服务器访问日志里属于肉眼可见的安全风险。另一个容易漏的配置是CSRF_TRUSTED_ORIGINS。如果部署到线上用了域名或域名加端口访问Django会校验请求的Origin头不在白名单里的域名一律拒绝POST请求。本地localhost没问题但用局域网IP地址访问时经常出现这个情况排查时看浏览器控制台的响应信息就能定位。5.3 静态文件、媒体上传与部署时的差异化配置开发环境里DEBUGTrue时Django会自动帮你处理静态文件所以你很少会意识到静态文件配置的复杂性。一旦把项目部署到服务器DEBUGFalse之后Django默认不再服务静态文件页面CSS和JS全部丢失这是最典型的部署事故。正确的做法是在settings.py里配置STATIC_URL、STATIC_ROOT和STATICFILES_DIRS项目里用static/目录存放自定义的CSS、JS和图片执行python manage.py collectstatic把各App里的静态文件收集到统一目录再配合whitenoise中间件在WSGI层直接托管静态资源。媒体文件也一样课程封面图通过MEDIA_URL和MEDIA_ROOT访问Nginx或反向代理需要把媒体目录也配置好否则上传的图片永远显示不出来这类问题在答辩现场出现一次就会非常被动。django-debug-toolbar是排查页面查询问题的利器安装后会在页面右侧显示当前请求执行SQL的条数和耗时我定位N1查询问题基本都靠它。但它最好只在开发环境启用否则线上会暴露数据结构和查询细节属于安全隐患。5.4 性能优化与并发场景的务实优化在教学平台里最频繁的操作是“课程列表页”和“成绩统计页”这两个页面也是性能优化收益最大的地方。除了前面说的select_related和prefetch_related之外列表页分页是必须做的我用Django自带的分页器实现每页20条课程数据避免一次全量渲染。数据库层面给Lesson.order、AnswerRecord.student_id这类高频查询字段加索引通常用db_indexTrue声明即可不需要手动写SQL。如果并发稍高比如答辩演示时几十个同学同时访问默认的开发服务器性能和稳定性都不够。我在项目里使用gunicorn做WSGI服务器配置4个worker进程静态资源交给WhiteNoise数据库保持SQLite但把日志级别调低。坦白讲SQLite在高并发写入场景下会锁库教师同时批改多个作业时会遇到database is locked所以如果项目真的希望展示“生产可用”我会建议在论文里把数据库迁移到PostgreSQL的方法写清楚演示环境继续用SQLite也没关系。这些优化看起来零散但都是在真实项目里被验证过的关键路径优化而不是理论堆砌。每次改完查询逻辑后我会打开Debug Toolbar对比SQL条数和页面响应时间把变化记录在项目的开发文档里后面写论文测试章节时这些数据就是现成的素材。老实讲做完这个Django少儿英语教学平台我最大的收获不是背熟了框架API而是理解了怎么把一个模糊的“少儿英语教学”概念拆解成课程、课时、题目、答题记录、作业、成绩、家长绑定这些能被代码和数据库表达的实体。后来我再看别人的课程设计和毕设项目一眼就能判断出对方是真正设计过还是只是把教程案例改了个皮。这也是我建议每个准备动手做这个项目的人先花两天时间把数据模型和角色权限理清楚的原因——想清楚了再写代码效率一定比边写边改高一倍。最后说一个我一直在用的习惯项目里每写一个功能就在本子上记一行“这个功能解决谁的什么痛点”攒到论文和答辩的时候你会发现所有章节的素材都已经就位了。