news 2026/10/2 9:48:48

Python+Django考研学习系统:从设计到实现全复盘

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Python+Django考研学习系统:从设计到实现全复盘

考研学习系统这个题目,在计算机毕设里算是一个“长跑型选手”:它看起来不难,但真做起来涉及的东西一点不少,注册登录、学习资料管理、题库刷题、学习计划打卡,还有数据分析展示,每一块都踩得到Django的真实功能。我基于 Python 和 Django 完整跑了一遍这套系统的源码和文档,配合代码讲解和一条龙定制把项目彻底理顺了,从设计思路到具体实现、再到后期答辩,这篇就做一个完整复盘。适合那些准备做毕设、想快速上手 Django 项目、或者想理解一个完整Web系统如何从零落地的同学参考,尤其是想选“在线学习/考研学习”这类题目的,可以直接对照这个框架来搭。

1. 项目整体设计与技术选型

1.1 题目怎么定,为什么说它有真实需求

大多数毕设选题都会纠结一个问题:到底选个热门技术方向,还是选个能自圆其说的业务系统?我的建议是选有真实场景、功能边界清晰的业务系统,考研学习系统恰好符合。

考研这个场景有个很明显的痛点:资料太多、计划散乱、刷题没有统计。今天下载一套英语真题,明天收藏一个政治笔记,再过几天刷了两套数学卷子,全凭感觉复习,效率很差。学习系统要解决的就是“让学习过程可视化、可管理”——把资料集中管理,把题库拆成模块,把刷题记录和正确率统计出来,把每天的学习计划变成打卡记录。这个需求在现实里是成立的,做出来之后功能和页面都有具体指向,不是那种为了凑功能的“虚系统”。

针对毕设而言,这个题目还有个好处:功能量级可控。太简单的系统比如“单一论坛”不好撑篇幅,太复杂的比如“大型电商”又超出个人能力。考研学习系统属于中间档位,既能体现用户管理、业务逻辑、数据交互,又能加数据可视化亮点,论文好写、答辩好讲。

1.2 为什么选 Django,而不是 Flask 或者 Spring Boot

技术栈选型是毕设答辩时老师几乎必问的问题。我在这套系统里选择了Python + Django,主要基于以下几点考虑。

Django最大的特点是“全家桶”:ORM、Admin后台、用户认证、表单处理、模板引擎都是内置的。这意味着开发一个业务系统,绝大多数底层能力不用重复造轮子,专注于业务逻辑本身。对于毕设这种有明确时间节点的项目,开发效率是第一位的。

对比一下其他常见方案更清楚:

方案优势劣势适合场景
Django自带后台、ORM强大、全栈开发快、Python语法简单框架较重、学习曲线略陡业务管理系统、信息管理系统
Flask轻量灵活、自由组合需要自己集成ORM、认证、后台小型接口服务、想展示底层能力
Spring Boot企业级、生态完善Java语言门槛高、配置复杂大型企业项目或Java方向毕设

如果你已经有Java基础,选Spring Boot没问题;但如果毕设周期短、想用Python、又希望系统能快速成型,Django是更稳的选择。答辩时老师问“为什么不用Flask”,你可以回答:系统除了接口还需要后台管理、权限控制、数据模型等大量完整功能,Django内置体系能保证项目的规范性和稳定性,更贴合实际开发中系统化交付的需求。说真话,Django的Admin后台也是加分项,很多演示环节可以直接在后台展示数据管理。

1.3 系统功能模块怎么拆,才能撑起整个毕设

我把系统拆成六大模块,每个模块对应一套完整的Django项目结构:

  1. 用户模块:注册、登录、注销、个人信息修改,就是把Django自带的认证体系扩展为考研用户角色。
  2. 学习资料模块:上传资料(PDF/Word/图片)、资料分类(政治、英语、数学、专业课)、资料列表与下载。
  3. 题库管理模块:题目的增删改查、按科目分类、选项设计,区分单选题和多选题。
  4. 刷题练习模块:随机刷题、按科目顺序刷题,提交答案即时判分,记录作答历史。
  5. 学习计划模块:制定每日学习计划、打卡完成、查看今天或本周未完成项。
  6. 数据统计模块:刷题总量、正确率、各科占比、打卡日历,用表格和简单图表展示。

模块之间不是孤立的,而是有数据关联的。例如刷题记录关联用户和题目,学习计划关联日期和完成状态,统计模块从刷题记录中聚合数据。这样数据库表之间产生外键关系,论文的“数据库设计”部分才有的写,答辩时也能讲明白系统不是东拼西凑。

用户权限也是一个巧妙的点:普通用户只能查看和上传资料、刷题;管理员通过Django自带的Admin后台或独立管理页面维护题目和资料。我建议普通系统不用把权限设计得太复杂,只要区分“登录用户可以干什么”和“游客不能干什么”,已经足够体现完整的权限控制概念。

2. 环境准备与项目初始化

2.1 Python、Django、数据库版本怎么选

版本选择是新手最头疼的问题,直接说结论:Python 3.8到3.10都没问题,Django推荐3.2 LTS或4.x系列,数据库开发阶段用Django默认的SQLite就够,部署时再切换MySQL或者PostgreSQL。

Django 3.2是长期支持版本,稳定、坑少、网上资料多,适合保守选择;Django 4.x在功能上更新,但需要注意第三方插件的兼容性。如果你的代码讲解或部署文档是基于某个特定版本写的,建议跟着文档版本走,不要在环境上强行追新。

数据库方面,SQLite的特点是零配置、单文件、开发调试方便。缺点是并发能力弱、部分复杂查询稍弱。毕设环境完全扛得住。如果你想用MySQL,需要在settings里配置后端,驱动推荐用pymysql,或者在requirements里加上mysqlclient。

依赖文件requirements.txt大概是这样的:

Django==3.2.25 pymysql==1.1.0 Pillow==10.0.0 django-cors-headers

Pillow是为了处理头像上传和图片字段,django-cors-headers如果在前后端分离模式下才需要,纯Django模板渲染可以不装。安装就一行命令:

pip install -r requirements.txt

建议直接新建一个虚拟环境,避免和其他项目产生包冲突。开发到一半发现包版本错乱是最浪费时间的事。

2.2 创建项目、app与基础配置

初始化Django项目非常简单,Django-admin工具熟悉后,整个过程五分钟搞定。先创建项目和app:

django-admin startproject kaoyan_system cd kaoyan_system python manage.py startapp user python manage.py startapp material python manage.py startapp question python manage.py startapp exam python manage.py startapp plan python manage.py startapp stats

app的划分和功能模块对应,而不是一个app装所有代码,这是Django项目规范的第一步。如果代码全堆在models.py和views.py里,后面每一项功能都可能互相干扰。

然后是settings.py的修改。必改的几个点包括:INSTALLED_APPS注册app,LANGUAGE_CODE设置为zh-hans,TIME_ZONE设置为Asia/Shanghai,MEDIA_URL和MEDIA_ROOT配置上传文件目录。注册app这一步漏了会直接报“No module named”之类的错误,新手最容易栽在这里。

时区问题说一下,Django默认时区是UTC,如果你不改,保存到数据库的时间会差8小时。改语言和时区的操作很简单,但是不熟的话很容易忽略,等到统计学习时间或者打卡日期时才发现问题。

静态文件配置是这样的:

# settings.py MEDIA_URL = '/media/' MEDIA_ROOT = os.path.join(BASE_DIR, 'media') STATIC_URL = '/static/' STATICFILES_DIRS = [os.path.join(BASE_DIR, 'static')]

主路由urls.py里,开发阶段需要单独处理media文件的访问:

from django.conf import settings from django.conf.urls.static import static urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)

这一步不做,资料上传后图片或文件无法通过URL访问,页面上就会看到破图。

2.3 数据库表设计:一篇文章讲清楚关系

数据库设计是论文的重点章节。我当初设计的时候,核心表就这么几张:用户表User(直接继承Django的AbstractUser)、学习资料表StudyMaterial、题目表Question、刷题记录表ExamRecord、学习计划表StudyPlan。

先看models.py的核心部分:

from django.db import models from django.contrib.auth.models import AbstractUser class User(AbstractUser): phone = models.CharField(max_length=11, blank=True, null=True) avatar = models.ImageField(upload_to='avatar/', default='default.png') class StudyMaterial(models.Model): CATEGORY_CHOICES = [ ('politics', '政治'), ('english', '英语'), ('math', '数学'), ('major', '专业课'), ] title = models.CharField(max_length=200) category = models.CharField(max_length=20, choices=CATEGORY_CHOICES) file = models.FileField(upload_to='materials/') description = models.TextField(blank=True) uploader = models.ForeignKey(User, on_delete=models.CASCADE) created_at = models.DateTimeField(auto_now_add=True) class Question(models.Model): category = models.CharField(max_length=20, choices=CATEGORY_CHOICES) stem = models.TextField(verbose_name='题干') option_a = models.CharField(max_length=500) option_b = models.CharField(max_length=500) option_c = models.CharField(max_length=500, blank=True) option_d = models.CharField(max_length=500, blank=True) answer = models.CharField(max_length=10, verbose_name='正确答案') analysis = models.TextField(blank=True, verbose_name='解析') created_at = models.DateTimeField(auto_now_add=True) class ExamRecord(models.Model): user = models.ForeignKey(User, on_delete=models.CASCADE) question = models.ForeignKey(Question, on_delete=models.CASCADE) selected_answer = models.CharField(max_length=10) is_correct = models.BooleanField(default=False) created_at = models.DateTimeField(auto_now_add=True) class StudyPlan(models.Model): user = models.ForeignKey(User, on_delete=models.CASCADE) title = models.CharField(max_length=200) plan_date = models.DateField() is_completed = models.BooleanField(default=False)

几个关键点解释一下:

第一,用户表继承AbstractUser而不是单独建表。这是Django最优雅的地方之一:既保留了自带的用户名密码、权限字段,又能扩展手机号、头像这些业务字段,登录逻辑完全不用重写。很多新手卡在“我如何自己写注册登录”上,实际上用Django自带认证加扩展字段是最省力的路径。

第二,外键关系要设计清楚。ExamRecord同时外键关联User和Question,这就构成“一个用户做了很多题,一道题被很多用户做过”的多对多关系,通过中间表表达是最规范的。StudyMaterial里的uploader外键,表示这个资料是谁上传的,删除用户时资料也随之删除,因为用了on_delete=CASCADE。

第三,日期字段的格式。StudyPlan的plan_date使用DateField而不是DateTimeField,因为学习计划是按天来算的;而刷题记录的created_at使用DateTimeField,因为需要精确到秒来分析某个时间段的刷题行为。

3. 核心功能模块实现:从注册登录到刷题判分

3.1 用户注册登录与Token认证

注册登录是几乎所有系统都有的功能,Django在这里提供了非常成熟的基础方案。用户注册视图的思路是获取表单数据、创建用户实例、自动登录跳转。登录视图直接用Django的authenticate和login方法。

from django.shortcuts import render, redirect from django.contrib.auth import authenticate, login from django.contrib.auth.models import User from django.contrib import messages 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) return redirect('dashboard') else: messages.error(request, '用户名或密码错误') return render(request, 'user/login.html')

关于登录状态保持,这里有一个容易忽视的问题。Django默认用session保持登录状态,后端会在数据库或内存生成session记录,浏览器通过Cookie存sessionid。对小项目来说,直接用session是最稳的,不用额外写代码。但如果你想用前后端分离模式,比如前端用Vue调用接口,那么更推荐用JWT或者Token机制。

我在实际项目中做定制时,经常用Token方案,最简单的做法就是在登录成功后自己生成一个token串,写入浏览器Cookie,同时把token存到用户表的token字段里。代码如下:

import uuid from django.http import JsonResponse def login_view_api(request): if request.method == 'POST': username = request.POST.get('username') password = request.POST.get('password') user = authenticate(username=username, password=password) if user: token = uuid.uuid4().hex user.token = token user.save() response = JsonResponse({'code': 0, 'message': '登录成功'}) response.set_cookie('token', token, max_age=7*24*3600, httponly=True) return response return JsonResponse({'code': 1, 'message': '登录失败'})

给Cookie设置Token时有几个细节:httponly=True可以让前端JavaScript读不到这个Cookie,降低XSS窃取Token的风险;max_age设置了7天过期,正好对应考研学生一周回来看一次学习记录的频率。

不过我必须说一句:如果你的项目是纯Django模板渲染,没有明显的前后端分离需求,那就老老实实用Django自带的session认证,简单可靠,论文写起来也顺畅。不要为了炫技而引入不必要的复杂度,答辩时老师问你“为什么用Token”,如果你答不上来反而扣分。

3.2 学习资料管理与文件上传

资料管理模块要解决两个问题:上传和分类展示。Django的FileField加上模板中的form表单,几行代码就能实现上传逻辑。

from django.shortcuts import render, redirect from .models import StudyMaterial from .forms import MaterialForm def upload_material(request): if request.method == 'POST': form = MaterialForm(request.POST, request.FILES) if form.is_valid(): material = form.save(commit=False) material.uploader = request.user material.save() return redirect('material_list') else: form = MaterialForm() return render(request, 'material/upload.html', {'form': form})

注意form接收request.FILES参数这一步。很多新手在文件上传时遇到“表单提交后没有文件”的问题,原因就是创建表单实例时忘了把request.FILES传进去。这是一个非常典型的低级错误,但几乎每个Django新手都会踩一次。

展示页面要做分类筛选,可以用Django的filter查询:

def material_list(request): category = request.GET.get('category', '') materials = StudyMaterial.objects.all() if category: materials = materials.filter(category=category) return render(request, 'material/list.html', {'materials': materials})

文件下载有一个需要提醒的点:如果直接用FileField的URL字段让人下载,下载路径会直接暴露在浏览器地址栏里,而且没有权限校验。更规范的做法是写一个下载视图,先判断用户是否登录,再通过Django的FileResponse输出文件内容。这样既隐藏了真实路径,又能做权限控制。毕设里加上这个细节,是很大的加分项。

3.3 刷题模块与自动判分

刷题是考研学习系统的核心功能,实现上分为两步:取题和判分。

取题逻辑可以有几种:从题库随机抽取,或者按科目顺序出题。我用的是按科目分类、随机抽取的方式,保证学生每次刷题面对的题目顺序不同:

from django.shortcuts import get_object_or_404 import random from .models import Question def get_question(request, question_id): question = get_object_or_404(Question, pk=question_id) return render(request, 'exam/question.html', {'question': question}) def random_question(request, category): questions = list(Question.objects.filter(category=category)) if not questions: return render(request, 'exam/empty.html') question = random.choice(questions) return render(request, 'exam/question.html', {'question': question})

判分逻辑是核心中的核心。学生提交答案后,系统要和Question的answer字段比较,同时写入ExamRecord表:

from django.shortcuts import redirect from .models import ExamRecord, Question def submit_answer(request): if request.method == 'POST': question_id = request.POST.get('question_id') selected = request.POST.get('answer', '').upper() question = Question.objects.get(pk=question_id) is_correct = (selected == question.answer.upper()) ExamRecord.objects.create( user=request.user, question=question, selected_answer=selected, is_correct=is_correct ) return render(request, 'exam/result.html', { 'question': question, 'selected': selected, 'is_correct': is_correct })

这里有一个值得注意的业务细节:做题记录不要用update_or_create,而是每次提交都新插入一条记录。因为一个学生会反复做同一道题,第一次做错了,第二次做对了,两条记录都值得保留。后面的统计模块正是依赖这些历史记录来计算正确率的变化趋势。

题目解析这个字段也别浪费。在结果页面把analysis展示出来,学生做错后能直接看到解析,这比单纯显示“对/错”更有学习价值,也让系统在演示时多一个内容点。

3.4 学习计划与数据统计

学习计划模块最简单,就是一个“待办列表”加打卡功能。但这里要讲的是统计模块的价值。统计模块不是花架子,它把刷题日志从“散乱的数据”变成了“可读的结论”。

我先用聚合查询从ExamRecord里拿到正确率和刷题量:

from django.db.models import Count, Avg from django.db.models.functions import Coalesce def stats_view(request): user = request.user total_count = ExamRecord.objects.filter(user=user).count() correct_count = ExamRecord.objects.filter(user=user, is_correct=True).count() accuracy = round(correct_count / total_count * 100, 1) if total_count else 0 category_stats = ( ExamRecord.objects.filter(user=user) .values('question__category') .annotate(total=Count('id'), correct=Count('id', filter=models.Q(is_correct=True))) ) return render(request, 'stats/dashboard.html', { 'total_count': total_count, 'correct_count': correct_count, 'accuracy': accuracy, 'category_stats': category_stats })

这里的Count配合filter条件做条件计数,是Django ORM一个很实用的小技巧。业务上算“各科目的正确题目数”,不需要先取记录再在Python里循环,一条查询就搞定。答辩时老师如果问“统计是怎么实现的”,这一句就能讲清楚。

如果是按天统计打卡数据,可以再配合日历插件。我建议在这个模块里把前端图表技术引入一下:统计页面加载后用ECharts画一个柱状图或饼图,展示各科刷题占比。ECharts只需要引用一个JavaScript文件,后端把category_stats输出为JSON,前端就能画图。这个功能视觉效果好,实现难度又不高,是我最推荐加的“亮点功能”。

4. 实操过程中的关键细节:ORM、Cookie、分页与安全

4.1 ORM查询与删除对象时最容易踩的坑

Django的ORM太方便了,方便到很多新手忘了它也是有限制的。这里集中说我操作中踩过的几个坑。

第一个坑是删除对象时数据“删不干净”。Django的delete()操作和级联删除依赖模型定义中的on_delete参数。我在设计StudyMaterial时,uploader使用了on_delete=CASCADE,那么删除一个用户后,他上传的资料也会被级联删除。如果设置成on_delete=SET_NULL,则需要外键字段允许为空。这两种选择直接影响数据完整性。有的同学设计外键时随意指定,看起来运行没问题,但到答辩演示“删除用户”时,数据库里残留了一大堆孤儿记录,页面或者统计数据就会出现莫名的空值。

第二个坑是get()方法的异常处理。用get()查询记录时,如果查不到或者查到多条,Django会直接抛异常,500错误没有商量的余地。在业务逻辑里,更安全的写法是:

record = ExamRecord.objects.filter(user=user, question=question).first() if record is None: # 处理未找到的情况 else: # 处理找到的情况

第三点是性能问题。如果查询外键关联的数据,列表页展示用户名和资料名,默认N+1查询会很明显拖慢页面。可以用select_related优化:

materials = StudyMaterial.objects.select_related('uploader').all()

这个优化在毕设数据量下感受不明显,但如果老师现场导入了大量测试数据,页面打开速度就是直观的体验分。select_related解决的是“外键对象在同一张SQL查询里提前加载”的问题,一句话能讲明白,是一个很容易展示的技术点。

4.2 分页、搜索与列表页优化的基本操作

列表页数据如果越来越多,比如题库里几百道题一次全渲染,页面就会变慢。用Django内置的Paginator可以轻松解决:

from django.core.paginator import Paginator def question_list(request): questions = Question.objects.all() paginator = Paginator(questions, 10) page_number = request.GET.get('page') page_obj = paginator.get_page(page_number) return render(request, 'question/list.html', {'page_obj': page_obj})

模板里遍历page_obj,然后渲染上一页/下一页的链接,这个模式在所有列表页都是通用的。分页解决的不只是性能问题,还有“用户体验”——学生一屏看到10道题比一次滚动几百道题舒服得多。

搜索功能,最简单但有效的方式是用icontains做模糊匹配:

keyword = request.GET.get('keyword', '') if keyword: questions = questions.filter(stem__icontains=keyword)

需要注意,icontains在SQLite里会翻译成LIKE,在MySQL里会翻译成LIKE,都是大小写不敏感的模糊查询。中文搜索完全没问题。如果要搜索多个字段,可以用Q对象组合条件:

from django.db.models import Q questions = questions.filter( Q(stem__icontains=keyword) | Q(analysis__icontains=keyword) )

这些都是Django在后面帮我们做了安全转义,避免了直接拼接SQL注入的问题。讲安全时,我经常和同学说:你能用ORM就用ORM,自己写原生SQL除非非常确定,否则很容易出安全问题。Django ORM生成的SQL已经做了参数化处理,是最基础的防线。

4.3 上传校验、CSRF与模板中的安全细节

网络安全在毕设里不是重点考察项,但至少要做到几点基础工作。

第一,CSRF保护。Django默认在模板表单中要求添加{% csrf_token %},如果你在模板渲染的POST表单中漏掉这个标签,提交时会得到403。这个不是bug,是你正好踩中了Django的防CSRF机制。如果你的某个视图要支持API方式POST,又不方便加CSRF Token,可以用@csrf_exempt装饰器,但我不建议无缘无故地去掉这个保护。讲答辩时,“我如何保证表单提交的安全性”是一个能拉开档次的问题。

第二,上传文件校验。如果允许用户上传任意类型文件,理论上存在上传恶意脚本的风险。最简单的校验是检查文件扩展名,同时限制大小:

def validate_file_extension(value): import os ext = os.path.splitext(value.name)[1] valid_extensions = ['.pdf', '.doc', '.docx', '.png', '.jpg', '.jpeg'] if not ext.lower() in valid_extensions: raise ValidationError('不支持的文件类型')

第三,图片上传路径的处理。我见过不少项目,上传图片之后不通过MEDIA_URL读取,而是直接在前端拼接路径,结果页面显示破图。正确做法是模板中通过{{ material.file.url }}动态获取文件地址。Django会结合MEDIA_URL自动生成完整URL,这才是在页面正常访问文件的正确姿势。

5. 常见问题排查与避坑实录

5.1 高频问题速查表

我在整理这套源码和做代码讲解的过程中,收集了同学反馈最多的问题,做成了下面这个快查表:

问题现象根本原因解决办法
登录后刷新就掉线没有正确调用login(),只在session里手动存了用户ID使用Django自带的login(request, user)方法
中文乱码数据库编码不是utf8/utf8mb4MySQL建库时指定utf8mb4,连接串加上charset参数
上传图片后无法访问忘了配置MEDIA_ROOT和MEDIA_URLsettings配置media路径,主urls里增加static(media)处理
表单提交一直403模板里漏了{% csrf_token %}在所有POST表单中添加csrf_token标签
时区差8小时settings里TIME_ZONE没设置成Asia/Shanghaisettings配置时区并确认USE_TZ情况
migration报错No migrations to applymodels改了但没生成迁移文件执行python manage.py makemigrations后再migrate
后台admin没有想要的表admin.py里没有注册模型在对应app的admin.py里用admin.site.register()注册
页面CSS样式丢失STATICFILES配置或DEBUG影响静态文件路由检查STATIC_URL与模板加载路径是否一致

这些问题的共性是“配置没到位”,不是逻辑多难。养成一个习惯:创建项目后先花十分钟检查settings.py里的时区、语言、静态文件和media配置,后面能省掉大半的坑。

5.2 我实际开发中经历的三个最值得分享的调试记录

第一个印象深刻的调试经历,是刷题统计中“正确率超过100%”的问题。现象是这样的:学生做对了一道题,统计页面的正确率会跳到100%以上。排查后发现,我的统计逻辑是把总记录数和正确记录数分开统计,但中途从ExamRecord表里手动删除了几条错误记录,导致正确记录数大于总记录数。真实原因是在测试阶段用delete()删数据时只删了部分记录,破坏了统计数据的完整性。

这个问题的解决思路很有代表性:数据统计功能应该始终以原始记录表为准,任何测试数据的清理都应该用统一的方式,不能靠手工半删不删。后来我在统计视图里加了防御逻辑,正确记录数超过总记录数时,直接强制让正确率等于100%。上线发现数据异常时的兜底处理,这也是一种工程思维。

第二个调试经历是Token过期失效的问题。给学生定制前后端分离版本的时候,登录状态经常失效,排查后发现是Cookie的path参数问题。我在set_cookie时没指定path,导致Cookie只在当前路径下生效,用户从首页跳转到其他页面时Cookie就“消失”了。解决方法是明确设置path='/':

response.set_cookie('token', token, max_age=7*24*3600, httponly=True, path='/')

这个细节很冷门,网上大多数教程都没提,但实际接项目的时候非常常见。如果你做一个多页面应用,Cookie的path必须好好设置。

第三个调试记录是文件上传到一半,发现文件名重复导致相互覆盖。默认的FileField上传文件时保留原名,如果两个同学上传了同名的“真题.docx”,后上传的会把前面的覆盖掉。解决办法有两个,一种是给upload_to加时间戳子目录,一种是重写文件保存方法。最省事的是用uuid作为文件名前缀:

def upload_to(instance, filename): ext = filename.split('.')[-1] return f'materials/{uuid.uuid4().hex}.{ext}'

这个问题在单用户测试时发现不了,一旦多人使用就会炸,属于经典后端问题。能在毕设答辩时主动讲出这个设计,比什么问题都等老师问的强。

5.3 部署到服务器时的几个小提醒

如果毕设要求不是本地演示而是部署到服务器,有几个点需要注意。

第一个是Debug模式。本地开发设置DEBUG=True没问题,但部署到公网时必须设成False,否则报错页面会直接输出详细信息,包括数据库连接、绝对路径和部分源代码,这在安全上非常危险。同时记得配置ALLOWED_HOSTS为你的域名或IP。

第二个是数据库切换。如果从SQLite换到MySQL,最常遇到的坑是Migrate时报错。解决办法是在__init__.py里加上:

import pymysql pymysql.install_as_MySQLdb()

这是一行代码解决Django连接MySQL驱动问题的经典做法。

第三个是静态文件。DEBUG=False后,Django不再提供静态文件服务,需要收集所有静态文件然后交给Nginx处理:

python manage.py collectstatic

这一步很多人会忘,结果部署后页面没有CSS样式,裸奔的HTML页面让整个项目看起来像半成品。我提醒所有做部署的同学,部署前先跑一遍Nginx静态文件配置,再开始演示。

6. 文档整理、答辩准备与二次开发方向

6.1 毕设文档怎么配合源码写

我很重视代码讲解和文档的配套,因为源码再完整,如果文档逻辑不清,答辩时依然很容易被问倒。毕设文档一般包括需求分析、系统设计、数据库设计、系统实现、系统测试这五大部分,但我觉得很多同学写文档有个通病:把代码抄一遍就当设计说明。

更好的写法是,用文字描述“为什么这么设计”,而不是“代码是什么”。比如数据库设计章节,除了贴字段,还要解释为什么ExamRecord是外键关联两张表而不是单独存用户名和题干。需求分析章节,除了说“学生可以刷题”,还要说清楚“刷题后系统记录答案、判断对错、更新统计,并在个人中心展示正确率变化”。这种因果逻辑是答辩的核心,也是论文和普通代码注释的区别。

按这个思路,我在整理源码时习惯每一份文档都带上一个“设计决策说明”段落,把关键设计的考虑写出来。这个习惯还帮自己在答辩时规避了很多“你这里为什么不做成那样”的提问——因为你已经提前说出了选择依据。

6.2 二次开发还能加什么亮点

如果学有余力,我给你三个扩展方向,按性价比排序。

第一个是错题本功能。把答题错误的题目自动收集到一个错题列表,下次刷题只刷错题。这正好就是“考研人”真实需求中的痛点。实现思路非常简单:ExamRecord里筛选is_correct=False,再根据question字段去重,一个视图加两个模板就能完成。

第二个是推荐刷题功能。基于用户薄弱科目推荐更多题目,不一定要做多高深的算法,只需要统计各科正确率,把正确率最低的科目的题目优先推送。这就算是一个“简单的个性化推荐”,作为论文创新点很容易讲清楚。

第三个是签到提醒与每日一题。可以用Django的定时任务或者简单的Celery实现每天给学生发一封邮件或者站内通知,提醒打卡,附带一道题目。这个功能适合做成亮点,因为涉及了任务调度和消息推送,答辩时有故事可讲。

我想强调一点:扩展功能并非做得越多越好。毕设评分更看重“完成度”和“逻辑自洽性”,一个错题本功能,从数据模型到页面展示再到统计汇总,能形成完整闭环,就比硬塞一个不完整的聊天室有价值得多。


这套项目从流程图到数据库设计、从页面原型到代码实现,整套闭环做下来,我自己最大的感受是:Django真正适合这种业务系统开发的场景,ORM和Admin后台省掉了很多底层功夫,让你有余力把业务逻辑和用户体验做细。如果一定要给后来者一个建议,我会说:拿到任何一套源码,先别急着改功能,先跑起来,用浏览器把所有页面都点一遍,然后打开Admin后台把数据增删改查一遍,最后再读代码。当你完整经历过这一遍之后,再回头看框架的设计,很多东西都会突然通。后面如果时间允许,我还会继续分享这个系统的前后端分离改造版和部署全流程,有需要的话可以先照着本文把基础版本跑通。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/2 9:48:48

不会聊天的AI:从对话式到任务执行型的Agent实战解析

看到这条消息的时候,我第一反应是挺惊讶的:参与打造 ChatGPT 的人,转身做了一个几乎不跟你聊天的 AI?ChatGPT 最出名的本事不就是对话吗?但在 AI 应用研发这个圈子里泡久了你会发现,这真不是一句玩笑&#…

作者头像 李华
网站建设 2026/10/2 9:48:03

AI重塑投资研究:从信息压缩到决策效率的进化

先聊一个真实感受:这两年投资圈里一个很有意思的分水岭,不是看多还是看空,而是"用AI的人"和"不用AI的人"之间的差异越来越明显。我自己从半信半疑到重度使用,体验最深的一点是——AI不会替你做投资决策&#…

作者头像 李华
网站建设 2026/10/2 9:47:44

SpringBoot校园资讯分享平台毕设实战:从选题到实现全攻略

每年到了毕业设计季,总有人带着同一个问题来找我:“学长,Java毕设到底做什么题目才稳?”问得多了,我干脆把最常推荐的一套整理出来——基于SpringBoot的校园资讯分享平台。这个题目在选题库里属于常青树,通…

作者头像 李华
网站建设 2026/10/2 9:47:44

GPT-Image 2.5实测:12种AI绘画玩法,朋友圈配图与提示词技巧全攻略

1. 这东西到底能干什么:我和GPT-Image 2.5较劲两周后的使用报告 先别急着划走。如果你是那种放假前三天就开始焦虑"朋友圈发什么"的人,那我这篇整理的东西,应该正好卡在你的痒处上。 两周前我搞到了GPT-Image 2.5的测试入口&#…

作者头像 李华
网站建设 2026/10/2 9:46:55

专科生必看:9个降AI率工具实测,论文AIGC检测轻松过

每年毕业季,最让专科生头疼的除了论文本身,还有那个越来越刁钻的“AI率”检测。我见过太多人把初稿交给工具一查,直接标红30%、40%以上,明明是自己一个字一个字写出来的东西,就因为用AI润过色、查过资料,就…

作者头像 李华
网站建设 2026/10/2 9:46:20

Python 3.9.7从下载到PyCharm配置:Windows环境变量与虚拟环境保姆级教程

自己刚学Python那会儿,对着“Python 3.9.7下载与Windows系统环境配置方法”这类标题折腾了一整天,下载装完打开命令行输入python却提示“不是内部或外部命令”,然后又在PyCharm里卡在解释器选择上,整个过程相当劝退。这篇文章就把…

作者头像 李华