ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

基于Python与Django的可视化学习系统开发实践

基于Python与Django的可视化学习系统开发实践 每年毕业季前后总有人把毕设项目发过来让我帮忙把关十个里面少说六七个是 Django Python 的组合而这套“基于 Python 的可视化学习系统”算是其中最有代表性的一类。它把后端开发、数据库设计、数据统计和前端图表展示串在一起技术上看着饱满老师也认关键是工作量可控一个应届生踏踏实实做一个月完全能啃下来。所以这次我把这个项目的完整拆解、核心实现和踩坑经验都整理出来给准备做类似题目的同学一个可以照着走的路子。如果你正处于选毕设题目的阶段或者已经在做 Web 方向的系统却不知道可视化部分怎么落地这篇内容应该能帮你省下不少瞎折腾的时间。全文不会只丢一堆源码让你自己看而是带着你从需求设计一路走到图表渲染把每一步为什么这么做的逻辑也讲清楚最后再挑几个最常见的翻车现场复盘一下。1. 毕设选题与项目思路拆解1.1 为什么可视化学习系统适合用 Django 来做选技术栈这件事毕设和真实项目不太一样。真实业务要看并发量、团队协作、部署成本毕设更多看的是技术覆盖面、开发效率、答辩的时候能不能讲出东西来。Django 在这几个维度上的表现非常均衡。首先Django 自带一套完整的后端基础设施。ORM 让你不用手写 SQL 就能完成建表和增删改查Admin 后台几乎零成本就给你一个数据管理界面用户认证体系登录、注册、会话、权限也是现成的。很多人低估了这几个内置能力的价值——做可视化学习系统这种业务本质上就是一个典型的 CRUD 应用套一层数据展示。其次可视化学习系统天然依赖数据统计而 Python 在数据处理上优势明显。从数据库里把学习时长、课程分类、每日趋势聚合出来用 Django ORM 的 aggregation 和 annotate几行代码就能拿到聚合结果再交给前端图表库去画整个过程非常顺滑。说白了如果选 Flask你得自己拼 ORM、表单、用户认证光这些配置就够忙活一阵如果选 Spring BootJava 的体量对不少非科班学生来说光是环境就劝退一半人。Django 正好卡在中间既不会让你从零搭框架又不会难到让你看不懂自己在写什么。而且招聘市场上 Python 岗位多毕设经历写 Django 项目面试也有话聊不至于做完了就丢进文件夹吃灰。1.2 系统核心功能设计从需求清单到数据表规划拿到题目先别急着敲代码第一步永远是画功能清单。可视化学习系统这个题目的核心关键词有两个一个是“学习”一个是“可视化”。围绕这两个词我把系统拆成四大模块用户模块注册、登录、个人信息维护。学习资源模块课程分类、课程列表、课程详情。学习记录模块用户记录自己的学习行为比如学了哪门课、学了多久、掌握程度如何。可视化统计模块把学习记录汇总成图表让用户直观看到自己的学习趋势、分类占比、总体时长。这个模块划分不是拍脑袋。它既保证了系统具备完整的业务闭环又让每个模块都有技术点可讲——用户模块对应 Django 认证机制资源模块对应 ORM 外键关系和分页查询记录模块对应表单提交和模型校验可视化模块对应聚合查询和图表渲染。老师看到这个结构第一印象就是“这个学生是真的理解系统该怎么设计的”。模块定了接下来是数据库设计。我建议建以下这些表数据表核心字段作用用户表继承 Django 的 AbstractUser扩展学号、头像支持认证和个性化分类表分类名称、描述对课程做归类课程表标题、封面、所属分类、难度学习资源的载体学习记录表用户外键、课程外键、学习时长、掌握度、笔记可视化的数据源学习记录表是整个系统的关键。它关联了用户和课程以后统计某个用户学了多长时间、学过哪些分类的课程、掌握度变化趋势都得从这张表里取值。外键关系设计好后面的聚合查询才能顺畅。如果一开始把学习记录设计成挂在用户下面的一张简单表后期做分类统计就会出现要么拆表、要么漫无边际的查询改写非常难受。2. 核心功能模块解析与关键设计2.1 用户认证与权限管理的坑与要点Django 自带的认证系统已经覆盖了登录、注销、会话管理但直接用默认 User 表做毕设项目会有个小问题默认表字段太少你没法记录学号这种个性化信息。正确做法不是去改 Django 源码而是写一个继承 AbstractUser 的用户模型然后在 settings.py 里告诉 Django 换用这个模型。这件事必须在第一次迁移之前做好不然等你已经生成过 auth_user 表再改迁移就会出现一堆冲突处理起来非常恶心。# settings.py AUTH_USER_MODEL accounts.User # accounts/models.py from django.contrib.auth.models import AbstractUser class User(AbstractUser): student_id models.CharField(max_length20, blankTrue, nullTrue, verbose_name学号) avatar models.ImageField(upload_toavatars/, blankTrue, nullTrue, verbose_name头像) class Meta: verbose_name 用户权限控制这块我建议分两个层级走。普通用户用 login_required 装饰器或者 LoginRequiredMixin 做视图级拦截确保没登录的人不能访问学习记录和统计页面。管理员的权限不用你自己造轮子直接使用 Django Admin 后台内置的 staff 和 superuser 机制。答辩的时候要能讲清楚为什么登录校验放在视图层而不是模板层因为模板层的判断只是 UI 层面的隐藏视图层的校验才是真正的数据安全边界。还有一个小细节你自己的系统就不要再去写一套 session 控制逻辑了Django 框架在你登录成功后会通过 session 和 cookie 维持状态你只需要在模板里用{% if request.user.is_authenticated %}控制导航栏显示“登录/注册”还是“欢迎XX/退出登录”。这一块的代码量不多但很容易答出彩老师问起你就说“这是借助了 Django 中间件和 session 框架实现的”然后配合简单图解讲一讲。2.2 学习记录模块的数据采集与校验学习记录是整个可视化的数据源头所以这个模块的健壮性直接决定了图表好不好看。我见过的低质量实现是前端让用户填“学习时长”文本输入框后端只存一个 Int 字段结果用户填“2.5小时”或者“两个小时”就直接崩了。稳妥做法是给表单限定好输入类型和范围。时长字段用 FloatField前端用 Number 类型的 input配合 Django Form 的验证器做范围校验。表单写完以后视图里再补一层逻辑如果这个用户已经对同一课程同一日期重复提交提示他“你今天的记录已存在可以更新而不是新增”避免统计时出现同一天重复累计的问题。# learning/forms.py from django import forms from .models import LearningRecord class LearningRecordForm(forms.ModelForm): class Meta: model LearningRecord fields [course, duration, mastery, note] def clean_duration(self): duration self.cleaned_data.get(duration) if duration and duration 0: raise forms.ValidationError(学习时长必须大于 0) if duration and duration 24: raise forms.ValidationError(单次学习时长不能超过 24 小时) return durationimplementation 方面学习记录列表建议用 ListView 配合LoginRequiredMixin因为列表需要按当前登录用户过滤数据直接在视图里做queryset LearningRecord.objects.filter(userself.request.user).order_by(-study_date)就行。新增和编辑用 CreateView 和 UpdateView省去手动处理 request.POST 的样板代码。这里有个好处Django 的类视图把 GET 渲染空表单和 POST 校验逻辑都封装好了你做毕设的时候既显得代码结构专业又不容易在提交逻辑里漏掉数据校验。2.3 可视化统计图表怎么选、数据怎么给可视化是这个项目的灵魂也是最容易出效果的展示点。先讲选型。常见的思路有两条线一是后端用 Pyecharts 生成图表 HTML 片段直接嵌入 Django 模板二是前端用 ECharts通过 Fetch 请求 Django 写好的 JSON 接口拿到数据再渲染。我强烈推荐第二种方案也即前后端通过 JSON 接口分离。原因有三个第一ECharts 的交互效果好鼠标悬停、图例切换、数据缩放都是现成的答辩演示时比静态图片好看太多第二接口化的设计意味着以后数据源变化不用改前端后端换个聚合查询就行这种分层思维本身就是加分项第三它的技术栈更贴近企业真实开发跟老师聊的时候你就能说“这个系统的数据接口是独立设计的前后端通过 JSON 解耦”这句话比任何代码都能体现工程素养。后端接口怎么写我给你一个标准化套路。创建一个 stats 视图函数取当前用户的学习记录用 ORM 的聚合方法计算出总数、分类占比、每日趋势# learning/views.py from django.db.models import Sum, Count from django.http import JsonResponse from django.contrib.auth.decorators import login_required from .models import LearningRecord, Category login_required def stats_overview(request): records LearningRecord.objects.filter(userrequest.user) total_duration records.aggregate(totalSum(duration))[total] or 0 total_records records.count() return JsonResponse({ total_duration: round(total_duration, 1), total_records: total_records, total_courses: records.values(course).distinct().count(), }) login_required def stats_category(request): records LearningRecord.objects.filter(userrequest.user) result (records .values(course__category__name) .annotate(totalSum(duration)) .order_by(-total)) data [{name: item[course__category__name], value: round(item[total], 1)} for item in result] return JsonResponse({category_data: data})这三个接口分别对应首页统计卡片的三个图表概览图、分类占比饼图、时间趋势折线图。你不需要把图表画在后端前端拿到 JSON 之后直接填进 ECharts 的 option 即可。这种“后端算数、前端画图”的模式非常契合毕设答辩的讲解逻辑先讲模型的关联关系再讲接口返回的 JSON 格式最后讲前端如何处理 JSON。另外要注意JSON 接口的返回内容一定不要包含多余字段前端代码里写的字段名必须和后端 key 完全一致不然图表很容易渲染出一片空白。我见过很多同学调了半天没反应最后发现是category_data写成了categoryData这种低级错误还不好排查建议你写接口时先打开浏览器访问一次 JSON 地址把数据格式直接看到确认无误后再写前端。3. 从零到一的实操流程与核心代码实现3.1 环境准备与项目初始化清单一个干净的起步环境能给你后面省掉大量麻烦。先说版本组合。虽然 Python 和 Django 的版本在不断往前跑但毕设项目追求的是稳定不是最新。我推荐 Python 3.10 或 3.11 搭配 Django 4.2 LTS这套组合的兼容性很好资料也多遇到问题一搜就有解决方案。Python 3.12 搭配 Django 5.x 也不是不行但有些第三方库还没有完全适配而毕设里你几乎一定会用到 Pillow 处理图片版本一冲突就头疼。环境搭建的几个关键命令我给你一条条列出来# 创建虚拟环境推荐放在项目目录下 python -m venv venv # Windows 下激活 venv\Scripts\activate # macOS / Linux 下激活 source venv/bin/activate # 安装核心依赖 pip install django4.2.* pillow安装依赖这一步如果你在配置 Python 时遇到 pip 下载超时可以用国内镜像源加速pip install -i https://pypi.tuna.tsinghua.edu.cn/simple django4.2.* pillow项目初始化分两步。先用 django-admin 创建项目再创建三个 app 分别放不同业务django-admin startproject learning_system cd learning_system python manage.py startapp accounts python manage.py startapp learning python manage.py startapp course这里的模块划分理念就是高内聚低耦合。accounts 管认证和用户模型course 管课程分类和课程资源learning 管学习记录和统计接口。所有业务代码都放在自己该在的 app 里不要把所有视图堆到同一个文件里。很多学生做毕设喜欢在views.py里一个文件写到尾代码五六百行迁移和查错都费劲答辩的时候也很难讲清楚。settings.py 里除了AUTH_USER_MODEL以外还有两个必须改的地方一是把语言时区改成中文二是配置静态文件目录。不改的话 Admin 后台是英文的ECharts 文件放进去也读不到。# settings.py LANGUAGE_CODE zh-hans TIME_ZONE Asia/Shanghai STATIC_URL /static/ STATICFILES_DIRS [BASE_DIR / static]3.2 核心模型与数据库迁移实操初始化完成之后下一步就是写数据模型并完成迁移。这个过程里最需要注意的是“首次迁移时间点”。前面已经提过AUTH_USER_MODEL 一定要在第一次migrate之前就配置好否则默认的 auth_user 表一旦生成再切换用户模型就需要额外的数据迁移操作很容易产生不一致。模型定义好之后迁移指令是毕设必背的python manage.py makemigrations python manage.py migratemakemigrations 是把模型变化生成迁移文件migrate 才是真正应用变更到数据库。这套流程被很多初学者误解成“一键同步数据库”但答辩时如果你能说出“Django 通过迁移文件来管理数据库 schema 的版本变化每个迁移文件就是一个可以追溯的变更记录”这就是一个很有深度的点。实际上迁移文件确实是可以被版本控制器追踪的文本文件团队成员拉取代码后执行 migrate 就能同步数据库结构这个机制是企业开发里非常重要的一环。如果你在迁移过程中碰到“AUTH_USER_MODEL 之前已经迁移过”的报错最省力的处理方法不是去手动改表而是删掉现有的数据库文件和所有迁移记录重新从头迁移。毕设阶段没有真实生产数据删库重来也是可以的总比在坏掉的迁移链路上硬修省事。代码写完之后再执行createsuperuser创建管理员账号用于登录 Admin 后台。3.3 可视化数据接口与前端图表联动后端接口写好了前端页面得把图表撑起来。我这里给你一个模板引用的完整思路。在模板文件的头部先加载 ECharts 的静态文件。如果你下载了 echarts.min.js 放到项目的 static/js 目录下模板里这样引用{% load static %} !DOCTYPE html html langzh-hans head meta charsetUTF-8 title学习数据统计/title script src{% static js/echarts.min.js %}/script /head body div idtrendChart stylewidth: 100%; height: 400px;/div script fetch({% url learning:stats_trend %}) .then(response response.json()) .then(data { const chart echarts.init(document.getElementById(trendChart)); chart.setOption({ title: { text: 近30天学习时长趋势 }, tooltip: {}, xAxis: { type: category, data: data.dates }, yAxis: { type: value }, series: [{ name: 学习时长(小时), type: line, data: data.durations, smooth: true }] }); }); /script /body /html这里有几个细节特别容易踩坑。第一个是容器高度ECharts 初始化的时候如果容器高度为 0图表会在页面上“隐形”你打开调试模式看到 div 存在但没有图表内容。所以图表容器一定要显式设置高度比如上面代码里的height: 400px。第二个是异步加载的顺序fetch 是异步的图表初始化必须放在 then 回调里否则接口数据还没回来你就要 setOption图表自然空白。第三如果你的统计接口涉及跨域前后端分离部署时常见记得安装并配置 django-cors-headers否则浏览器会把请求拦截掉控制台会报 CORS error。多图表页面我建议把每个图表初始化封装成独立的函数比如initTrendChart()、initCategoryChart()、initOverviewCards()然后统一在window.onload里调用。这样做的好处是代码结构清晰也方便逐个调试。答辩现场最怕的就是页面整个白屏函数拆开以后你至少能定位是哪个请求失败不会一脸懵。3.4 管理后台、页面路由与整体运行闭环前后端联调完成之后再把管理后台和路由做完整整个系统的运行闭环就通了。Django Admin 不是只用来给管理员看它还是你往系统里填充数据的快捷渠道。在 learning/admin.py 和 course/admin.py 里注册模型# learning/admin.py from django.contrib import admin from .models import LearningRecord admin.register(LearningRecord) class LearningRecordAdmin(admin.ModelAdmin): list_display (user, course, duration, study_date, mastery) list_filter (user, course, study_date) search_fields (note,)Admin 配置好之后往课程表和分类表里添加几条演示数据就很容易了。毕设演示的时候数据越真实越好别只放一两条空数据那样图表看起来稀疏自问自答也不好看。至少准备 5 个分类、每类 2 门课程学习记录放上个把月的量图表才有说服力。URL 路由的设计也要规整。我在 projects 里的习惯是项目根 urls.py 只做 app 分发每个 app 自己的 urls.py 管自己的路由并用app_name设置命名空间方便模板里用{% url %}反向解析。# learning_system/urls.py from django.contrib import admin from django.urls import path, include urlpatterns [ path(admin/, admin.site.urls), path(accounts/, include(accounts.urls)), path(course/, include(course.urls)), path(learning/, include(learning.urls)), path(, include(learning.urls)), # 首页入口 ]最后python manage.py runserver跑起来完整路径应该是用户注册 → 登录 → 浏览课程 → 记录学习 → 查看统计图表 → 管理员后台维护数据。这条主流程顺了你的毕设就算立住了后面做的都是锦上添花的扩展比如导出学习报告、添加学习计划、分享学习笔记之类的功能。4. 常见问题与排查技巧实录4.1 环境安装与版本兼容问题被问到最多的就是 Python 环境和装包的坑。第一类是 pip 安装卡住或者权限不足。Windows 上如果提示“externally-managed-environment”那是新版本 Python 的 PEP 668 限制让它别在系统环境里直接装包建议你用 venv 虚拟环境就不会有这样的问题。第二类是版本冲突比如装了 Django 5.x 之后发现某个教程里的写法变了运行报错找不到模块。我的建议是直接把 Django 版本锁死到 4.2 LTS因为 4.2 是长期支持版本网上资料和课程资源匹配度最高。版本对应关系给大家整理成一张速查表Python 版本推荐 Django 版本说明Python 3.8Django 3.2 / 4.0老环境尽量升级 PythonPython 3.9Django 3.2 / 4.1稳定性尚可Python 3.10Django 4.2 LTS毕设首选搭配Python 3.11Django 4.2 / 5.0兼容性已很成熟Python 3.12Django 5.0个别扩展库可能有坑如果装包时报错先看清楚报错信息尾部两行十有八九是缺了某个编译依赖比如python3-dev或者libjpeg。Windows 里装 Pillow 一般没问题Linux 下如果 Pillow 装不上多半是缺 zlib 或 libjpeg搜索对应系统名加上报错关键词就能找到安装命令。4.2 数据迁移和外键关联的连环坑Django 的迁移机制虽然好用但用错时机就非常痛苦。最常见的场景是学生先跑了 migrate生成了默认的 auth_user 表然后才想起来要自定义用户表在 models.py 里改了 AbstractUser再跑 makemigrations 发现 Django 要求你处理一遍复杂的依赖冲突。遇到这种情况不要慌毕设阶段最干脆的解法就是重置。把 db.sqlite3 文件删掉把每个 app 的 migrations 目录下除了__init__.py之外的迁移文件也删掉然后重新makemigrations migrate createsuperuser。只要你还没有录入大量测试数据这个操作成本很低值得优先考虑。外键关联的坑主要出现在删除操作上。Django 默认on_deletemodels.CASCADE意味着删除一个分类它会删掉该分类下的所有课程课程删除又会删掉对应的学习记录。如果你只想删除分类但保留课程记录就把外键的 on_delete 改成PROTECT或者SET_NULL并且在关联字段上加nullTrue。这个选择在毕设里经常被老师追问建议你在 model 里加一行注释说明自己为什么用 CASCADE 或 PROTECT。4.3 图表白屏与数据接口报错排查图表白屏是可视化系统里出现率最高的故障没有之一。按照这个顺序排查基本 5 分钟内能找到问题第一打开浏览器开发者工具切到 Network 面板刷新页面看统计接口是否返回 200。如果接口报 404检查 URL 名在模板里写的和 urls.py 里注册的是不是一致如果 500看控制台的报错堆栈多半是 ORM 查询里写错了字段名。第二直接访问接口地址看返回的 JSON 结构是否完整。如果返回空列表说明数据库里没有数据赶紧通过 Admin 后台补几条。第三确认前端拿到的字段和 ECharts 的 option 字段能不能对上。我遇到过一个很经典的错误后端返回的是dates和durations前端写的是date和duration结果图表的坐标系直接崩掉。第四确认图表容器有显式高度。ECharts 在容器高度为 0 时不会绘制任何东西且不报错很坑。第五如果图表区域出现了但数据是空白的检查 series 里的 data 是不是数组以及 xAxis.data 是不是字符串数组。数值类型和字符串类型混用也会导致坐标系展示异常。接口调试期间你还可以安装 django-extensions 的shell_plus在命令行直接测查询语句的结果比一次次刷新页面看效果快得多。这个工具毕业后写 Python 也会经常用到早用早习惯。4.4 代码讲解与后续扩展建议源码分享出去之后很多人问“答辩的时候我该按什么顺序讲”。我的建议是先讲题目和需求再用一张系统架构图说明模块关系然后直接从数据库设计切入按“用户表 → 课程表 → 学习记录表”的顺序画 ER 图讲清楚外键关联的含义接着进入代码讲解先展示 ORM 的聚合查询再展示 JSON 接口的返回最后打开前端页面让图表动起来。不要一上来就打开代码编辑器逐行念代码那样老师和听众都困。要带着问题讲代码比如“如何获取一个用户最近 7 天的学习时长”然后打开视图函数展示 annotate 和 filter 的用法最后把结果渲染到页面上验证。这种从问题到代码到效果的讲法是答辩里最不出错的节奏。后续扩展方面如果时间充裕可以往三个方向延伸一是增加在线视频学习模块把学习记录和视频播放进度绑定让数据采集更自动化二是引入简单推荐策略根据用户分类偏好推荐对应课程三是导出学习报告为 PDF这正好能用到 Python 生态里的 pdf 生成库。扩展的点子不需要多挑一个做出来就能在答辩“未来展望”环节打得很漂亮。最后分享一点我在实操中的体会可视化学习系统这类题目真正拉开差距的不是图表多炫酷而是数据从哪里来、怎么保证数据有效、统计口径是否合理。把这三件事想明白代码实现反而是水到渠成的事。如果你正在做类似的 Django 项目建议先跑通“录入一条学习记录 → 接口返回汇总数据 → 图表展示”的最小闭环再考虑加花活。这个闭环通了整个系统的主干就稳了。
返回列表