
简介面向Python课程设计与Django入门学习者这套基于Django的学生心理测试系统是一份可直接运行的完整课程设计项目覆盖心理测试题目管理、答题评分、结果反馈、用户登录与后台管理等典型功能属于老师指导并通过的高分作品。资源共121个文件以Python源码为主48个.py另有39个.pyc编译文件、4个.sql数据库脚本、7个HTML页面与5个CSS样式文件以及17张用于测试页面的图片素材整包仅1.83MB轻量紧凑下载后即可导入数据库并启动服务。项目目录遵循Django常规分层模型、视图、模板、静态资源划分清晰便于研读MTV架构、ORM查询与会话控制等关键知识点也可快速改造为其他测评类系统。已有2657人学习使用非常适合作为课程设计大作业、答辩演示或Django综合练习的参考模板。1. 一次课程设计把“心理健康”做成可运行系统学生心理测试系统几乎每年都会出现在 Python 课程设计的选题列表里原因是它的功能边界足够清晰——量表展示、测试作答、结果评分再加一个管理后台——但每个环节又能扩展出不少真实工程问题。用 Django 来做这个题目核心优势不只是“自带后台管理”和 ORM 写起来省事而是 MTV 架构天然地把“测试题目维护”和“用户作答流程”拆成了两个相对独立的模块数据模型理清楚之后后面的路由、视图、模板只是按部就班地填肉。本文从数据库表结构设计讲起手把手把一套可作为课程设计提交的方案落地模型层怎么建、量表怎么管理、评分算法放哪个位置、Django Admin 怎么把题目编辑功能白送给你最后给出答辩前必查的清单和常见扣分点。适合三类人阅读选了类似题目但还没想清楚数据模型的在校生需要快速搭一套内部心理测评工具但不想从零写表单校验的 Python 工程师以及负责指导课程设计、想给学生提供一套“能跑通且有扩展空间”的参考实现的老师。2. Django 学生心理测试系统的数据模型设计2.1 先想清楚实体关系量表、题目、选项、作答记录常见的设计失误是一上来就建一张大表字段从学号一直排到“第 50 题答案”写完才发现没法扩展。心理测试系统的核心实体其实只有四类量表Scale、题目Question、选项Option、作答记录AnswerRecord。它们的关系是一个量表包含多道题目一道题目包含多个选项一个学生的一次测试产生一条作答记录而作答记录又和具体题目、具体选项关联。用 Django 的 ORM 来表达最外层的模型不需要写任何外键字段关系全部挂在子表上直接映射成数据库里的外键列。以经典的 SCL-90 症状自评量表为例90 道题每题 5 个选项如果按“每题一个数字字段”的扁平设计后端代码会写死到无法维护换一张量表就要改表结构。正确做法是把量表抽象成配置数据题目和选项都是独立的行记录。# models.py from django.db import models class Scale(models.Model): name models.CharField(量表名称, max_length100) desc models.TextField(量表说明, blankTrue) is_active models.BooleanField(是否启用, defaultTrue) created_at models.DateTimeField(auto_now_addTrue) class Question(models.Model): scale models.ForeignKey(Scale, on_deletemodels.CASCADE, related_namequestions) text models.CharField(题目内容, max_length500) order models.PositiveIntegerField(排序, default0) class Meta: ordering [order] class Option(models.Model): question models.ForeignKey(Question, on_deletemodels.CASCADE, related_nameoptions) text models.CharField(选项文本, max_length100) score models.IntegerField(分值, default1) order models.PositiveIntegerField(排序, default0) class Meta: ordering [order]这套模型把“一份量表长什么样”完全变成了数据库里的数据。要做第二份量表不需要动代码只需要往三张表里插入记录。on_deletemodels.CASCADE保证删除量表时级联清理题目和选项避免产生孤儿数据。related_name让反向查询语义清晰scale.questions.all()拿到全部题目question.options.all()拿到全部选项。2.2 作答记录的两种设计方案推荐 JSON 存储明细作答记录是心理测试系统里最容易设计过度的一张表。一个 90 题的量表如果每题都建一行作答明细一次测试会产生 90 行记录学生表、班级表再关联进来查询“某学生最近一次测试结果”需要连三张表。对课程设计来说这个复杂度不必要对真实系统来说90 题的明细行也不算大但没必要为“理论上的严谨”付出多表联查的代价。常见的做法是一张主表存一次测试的概要答案解析结果用 JSON 字段直接存进去明细表只在需要做“逐题分析报告”时才补充。class AnswerRecord(models.Model): student models.ForeignKey(auth.User, on_deletemodels.CASCADE, related_nametest_records) scale models.ForeignKey(Scale, on_deletemodels.PROTECT) answers models.JSONField(作答明细, defaultdict) total_score models.IntegerField(总分, default0) result_text models.TextField(测评结果, blankTrue) created_at models.DateTimeField(auto_now_addTrue) class Meta: ordering [-created_at]answers字段用 JSON 存的是{题目ID: 选项ID}的映射后端拿到后可以随时重新计算总分也能在模板里循环输出逐题对照表。Django 3.1 内置JSONField底层在 SQLite 里是文本存储在 PostgreSQL 里是原生 JSONB不需要额外安装扩展。选择这个方案后代码里不出现任何一张“逐题明细表”这是刻意的——因为课程设计提交的数据库文件要保持简洁源码包里的db.sqlite3给别人打开时三张核心表就能看懂整个系统的数据结构。2.3 用 Django 迁移管理数据库版本数据模型定稿后迁移文件就是课程设计源码包里最容易被忽略但最加分的部分。很多人直接删掉migrations目录再python manage.py makemigrations重新生成一份0001_initial.py这没错但如果做到一半改过模型迁移历史里能看到整个演进过程这才是“版本管理意识”的体现。python manage.py makemigrations psychology python manage.py migrate# 如果想在提交前重建一个干净的数据库文件 rm -f db.sqlite3 python manage.py migrate迁移到 SQLite 的物理表现就是源码包里的db.sqlite3文件。答辩时老师问“数据库在哪里”直接把db.sqlite3路径指出来再用 SQLite 工具打开看一下三张表的字段胜过任何口头解释。如果课程设计要求提供“数据库设计文档”那就把python manage.py sqlmigrate psychology 0001的输出复制到文档里这是 Django 自动生成的真实 SQL比手写更不容易出错。3. 测试流程与评分逻辑的代码实现3.1 答题页的会话处理不要求登录也能测学生心理测试系统的典型使用场景是机房统一开测如果强制每个学生先注册账号再登录体验差且浪费时间。常见做法是允许匿名作答用 Django session 记录“本次测试到哪一题了”提交时再要求学生填写学号姓名。这样既保证了流程顺畅又不会把匿名数据污染到用户表里。# views.py from django.shortcuts import render, redirect from .models import Scale, Question, Option, AnswerRecord def test_start(request, scale_id): scale Scale.objects.get(idscale_id, is_activeTrue) request.session[scale_id] scale_id request.session[answers] {} return redirect(test_question, scale_idscale_id, q_index0) def test_question(request, scale_id, q_index): scale Scale.objects.get(idscale_id) questions list(scale.questions.all()) total len(questions) q questions[q_index] if request.method POST: option_id request.POST.get(option) if option_id: answers request.session.get(answers, {}) answers[str(q.id)] int(option_id) request.session[answers] answers if q_index 1 total: return redirect(test_question, scale_idscale_id, q_indexq_index 1) return redirect(test_submit, scale_idscale_id) options q.options.all() current q_index 1 return render(request, psychology/test_question.html, { question: q, options: options, current: current, total: total, progress: round(current / total * 100), })request.session在这里承担了临时存储的角色answers在 session 里的形态和最终写入AnswerRecord.answersJSON 字段的形态完全一致提交时不需要做格式转换。这种做法的好处是即使学生中途关闭浏览器重新打开session 还在默认两周有效点继续答题能回到中断位置。list(scale.questions.all())把 QuerySet 转为列表是因为在循环里多次访问同一个 QuerySet 会重复查询数据库转成列表只查一次。3.2 评分算法放后端不把分数判断写进模板心理量表评分最常见的方法是把所有题目的分值累加再把总分和预设的常模分数区间对比落到对应等级。关键设计决策是常模区间要放在数据库或者一个独立配置模块里不要写死在视图函数的 if 判断里否则换一张量表就要改视图代码。# services.py def calculate_result(scale, answers): total 0 details [] questions scale.questions.prefetch_related(options) for q in questions: opt_id answers.get(str(q.id)) opt q.options.filter(idopt_id).first() score opt.score if opt else 0 total score details.append({question: q.text, option: opt.text if opt else 未作答, score: score}) if scale.name SCL-90: # 总分超过 160 或阳性项目数超过 43建议关注 positive_count sum(1 for d in details if d[score] 2) if total 160 or positive_count 43: result_text 需要关注建议预约心理咨询。 else: result_text 总体状态良好请继续保持规律作息。 else: result_text 测试完成感谢参与。 return total, result_text, details评分函数放在services.py而不是视图内是为了让单元测试可以绕过 HTTP 层直接调用。prefetch_related(options)在这里有实际意义90 道题如果逐题查询选项会产生 91 条 SQL预取后只产生 2 条。课程设计里不一定会被问到这个细节但在代码注释里写一句“用预取避免 N1 查询”能直接拉开和普通作业的差距。3.3 查看历史结果的视图与 URL 配置测试提交后生成一条AnswerRecord学生最关心的就是“我到底测出了什么”。历史记录页面按时间倒序列出最近几次测试点击可以展开看明细。这部分的 URL 配置虽然基础但要保证/test/1/这样的路径和/record/5/这样的路径不冲突。# urls.py from django.urls import path from . import views app_name psychology urlpatterns [ path(scale/int:scale_id/, views.test_start, nametest_start), path(scale/int:scale_id/question/int:q_index/, views.test_question, nametest_question), path(scale/int:scale_id/submit/, views.test_submit, nametest_submit), path(record/int:record_id/, views.record_detail, namerecord_detail), ]视图层的test_submit从 session 里取答案、调calculate_result、创建AnswerRecord、最后清空request.session[answers]。注意不能清掉scale_id因为得分页还要展示量表名。代码里用一个session.pop(answers, None)只移除答案保留其他字段这种细粒度操作在答辩时也能说清楚。4. Django Admin 与数据可视化让系统不止于“能跑”4.1 用 Admin 管理量表和题目Django 自带的后台管理功能对课程设计来说是最划算的加分项——不需要写一行前端代码就能获得量表、题目、选项的增删改查界面。默认的ModelAdmin只显示id和str但对嵌套了外键的模型得自定义列表页和编辑页否则作为课程设计提交会被老师质疑“功能完整度”。# admin.py from django.contrib import admin from .models import Scale, Question, Option, AnswerRecord class OptionInline(admin.TabularInline): model Option extra 3 class QuestionAdmin(admin.ModelAdmin): list_display (id, scale, text, order) list_filter (scale,) inlines [OptionInline] class AnswerRecordAdmin(admin.ModelAdmin): list_display (id, student, scale, total_score, created_at) list_filter (scale, created_at) date_hierarchy created_at readonly_fields (answers, total_score, result_text) admin.site.register(Scale) admin.site.register(Question, QuestionAdmin) admin.site.register(Option) admin.site.register(AnswerRecord, AnswerRecordAdmin)OptionInline嵌入到QuestionAdmin里后台录入一道题时直接在同一页面把 3 个选项填完不用来回跳转。readonly_fields用于作答记录防止管理员手动改分数——心理测试的分数应该由系统算出来而不是人工修正。date_hierarchy会根据created_at自动生成一个按日期钻取的时间轴数据量大时后台筛选效率明显提升。4.2 给测试结果加一个简单的可视化页课程设计要求“界面友好”最直接有效的不是引入 ECharts 或其他前端图表库而是用纯 HTML CSS 做一个结果条。Django 模板里可以直接计算百分比宽度不需要任何 JavaScript。div classresult-bar div classresult-bar-inner stylewidth: {{ score_percent }}%;/div /div p您的总分{{ record.total_score }}/p p评估结果{{ record.result_text }}/p后端给模板传一个score_percent把总分映射到 0-100 的百分数。SCL-90 的总分范围一般是 90-450用(total - 90) / 360 * 100算出比例。这个设计不占用任何额外依赖只是模板渲染层面的简单变换但在提交源码时不会因为引入了前端库而让项目体积显得臃肿。真正的图表展示不是课程设计的核心目标把“结果解读”用文本表达清楚优先级更高。4.3 导出测试数据为 CSV老师最爱检查的功能一个容易被问到但很多人没做的功能是“数据导出”。用 Django 的HttpResponse配合 CSV 模块在后台加一个 action把筛选出的作答记录导出成 Excel 可打开的文件实现成本二十分钟但带来的完整度提升非常明显。import csv from django.http import HttpResponse def export_records_csv(modeladmin, request, queryset): response HttpResponse(content_typetext/csv; charsetutf-8) response[Content-Disposition] attachment; filenametest_records.csv writer csv.writer(response) writer.writerow([学号, 量表, 总分, 结果, 日期]) for obj in queryset: writer.writerow([obj.student.username, obj.scale.name, obj.total_score, obj.result_text, obj.created_at.strftime(%Y-%m-%d %H:%M)]) return response export_records_csv.short_description 导出所选记录为 CSVCSV 文件需要utf-8编码并给出Content-Disposition头这样浏览器会直接触发下载。课程设计的源码包里有这个功能在文档里加一句“管理员可一键导出测试数据进行初步统计”就能把系统从“个人工具”抬升到“可以给辅导员用的管理平台”级别。注意 Django 4.x 及以上版本默认不要求django_extensions这个功能只用标准库的csv就能完成没有任何兼容性问题。5. 答辩验收清单数据库文件的完整性与可复现性以python课程设计基于Django的学生心理测试系统源码数据库.zip作为交付形态时核心评判标准不是功能多炫酷而是另一个人拿到压缩包后能否三步跑起来。# 步骤一解压后确认项目结构包含 manage.py 和 db.sqlite3 # 步骤二安装依赖 pip install django # 步骤三启动并访问 python manage.py runserverdb.sqlite3文件里至少要预置一条 SCL-90 量表数据、配套的 90 道题和每题 5 个选项以及至少 2 条模拟作答记录。如果答辩现场直接打开题目管理页是空的系统再怎么演示也会被扣分。准备数据的过程可以用python manage.py shell批量插入不要在代码里写死 JSON 导入保持数据库和代码的边界清晰。需要重点检查的细节包括settings.py里ALLOWED_HOSTS是否配置为[*]本地演示时需要、DEBUG是否保持True课程设计没必要关展示报错页反而显得项目未完成、sqlite3文件权限是否为可写只读文件会导致每次提交都报错。源码包里不要包含__pycache__目录和虚拟环境文件夹.gitignore要在提交前就配好这是工程素养的直接体现。答辩时建议演示三条路径管理员登录后台新建一张量表并添加题目学生匿名完成一次 5 题的短测试并查看结果在记录列表中导出 CSV。这三条路径分别对应数据建模能力、流程设计能力和后台功能完整性覆盖了课程设计评分的绝大部分权重。最后把db.sqlite3用 SQLite 浏览器打开给老师看表关系图ER 图在文档中手画一份本次课程设计在数据层面就站得住脚了。本文还有配套的精品资源点击获取