首页 / 资讯中心 / 文章详情

Django+Vue学科竞赛管理系统:全栈开发与毕设实战指南

Django+Vue学科竞赛管理系统:全栈开发与毕设实战指南 ★ FEATURED ARTICLE
简介本资源为学科竞赛管理系统毕业设计全套资料面向计算机相关专业学生及需要完成课程设计、毕业设计的学习者解决竞赛信息人工管理效率低、赛项报名与奖项统计缺乏统一平台的问题。系统基于Python语言采用Django后端与Vue前端框架配合MySQL数据库开发整体运行稳定、界面友好具备良好的可扩展性与可维护性。压缩包共744个文件约42.82MB涵盖47个py后端源码、38个vue前端组件、31个html页面、46个css样式、155个js脚本及2个sql数据库脚本另附毕业论文、视频演示与安装运行批处理文件便于快速部署与理解项目结构。功能上分为管理员与学生两大模块管理员可管理学生、教师、赛项信息、赛项报名、奖项统计及系统设置学生可浏览赛项与新闻资讯注册登录后进行个人中心管理与赛项报名。目前已有411人学习下载适合作为毕业设计参考与二次开发基础。1. 学科竞赛管理系统一套能跑通的毕设全栈方案长什么样每年到了毕设选题季总有一批同学在「做网站」和「做算法」之间反复横跳。选算法吧数据集跑不动、显存不够、调参调到怀疑人生选网站吧又怕太简单被导师一句「工作量不够」打回来。学科竞赛管理系统这个题目恰好卡在一个很舒服的位置业务逻辑足够复杂能撑起工作量技术栈又足够标准Python Django Vue MySQL 这套组合网上资料多、踩坑记录全遇到问题基本都能搜到答案。这套系统要解决的核心问题其实很具体一所学校每年有几十项竞赛从报名、组队、审核、缴费、作品提交到成绩公示全靠 Excel 和微信群在传教务处老师一个人要对接几百个学生。系统要做的就是把这些环节搬到线上让管理员建赛、学生报名、教师评审各走各的流程。适合谁做适合有一点 Python 基础、想拿一个完整全栈项目练手、同时需要一份能写进毕业论文的工程实践的同学。下面我按实际开发顺序把选型、建库、接口、前端、部署和避坑一条条拆开讲。2. 技术选型与数据库设计为什么是 Django Vue 而不是别的2.1 后端选 Django 的三个现实理由很多人一上来就问「Django 和 Flask 选哪个」这个问题在毕设场景下其实有标准答案。Flask 更轻但轻意味着什么都要自己搭用户认证、后台管理、ORM、表单验证全得手写。Django 把这些都内置了尤其是 admin 后台建完模型直接就能在浏览器里增删改查答辩演示的时候光这个后台就能撑五分钟。第二个理由是 ORM。学科竞赛管理系统里表关系不简单一个竞赛有多个赛道一个赛道有多支队伍一支队伍有多个成员成员又关联到学生表。用 Django 的ForeignKey和ManyToManyField描述这些关系查询的时候用select_related和prefetch_related优化比手写 SQL 清爽得多。第三个理由是生态。Django REST framework 做接口、django-cors-headers 处理跨域、djangorestframework-simplejwt 做令牌认证这些都是成熟轮子装完配几行就能用。我一般会先把这几个包装上pip install django djangorestframework django-cors-headers djangorestframework-simplejwt mysqlclient装mysqlclient的时候在 Windows 上容易翻车报Microsoft Visual C 14.0 is required解决办法是去装一个 Visual Studio Build Tools或者干脆换成pymysql并在__init__.py里加两行伪装代码。这个坑后面避坑章节还会细说。2.2 MySQL 建库与字符集的两个关键设置数据库这块MySQL 8.0 和 5.7 都能用但字符集一定要在建库的时候就定死不然后面存中文竞赛名称出现乱码改起来很麻烦。建库语句我习惯这么写CREATE DATABASE competition_db DEFAULT CHARACTER SET utf8mb4 DEFAULT COLLATE utf8mb4_unicode_ci;utf8mb4而不是utf8是因为 MySQL 里的utf8其实只有三个字节存不下某些生僻字和 emoji。竞赛名称里万一有个特殊符号用utf8就会截断报错。utf8mb4_unicode_ci这个排序规则对中文排序也更友好。建完库之后在 Django 的settings.py里配置连接DATABASES { default: { ENGINE: django.db.backends.mysql, NAME: competition_db, USER: root, PASSWORD: 你的密码, HOST: 127.0.0.1, PORT: 3306, OPTIONS: { charset: utf8mb4, }, } }OPTIONS里的 charset 必须和建库时一致否则 Django 连接时会用默认字符集照样乱码。这一步看着简单但我见过太多人栽在这里。2.3 核心表结构设计从竞赛到成绩的完整链路学科竞赛管理系统的表不用设计得太花哨抓住主干就行。核心表大概六张用户表区分管理员、教师、学生三种角色、竞赛表、赛道表、队伍表、报名记录表、成绩表。用户表可以直接用 Django 自带的AbstractUser扩展加一个role字段区分身份。竞赛表和赛道表是一对多赛道表和队伍表是一对多队伍表和报名记录是一对多。成绩表挂在队伍上一个队伍一条成绩记录。这里有个设计细节报名记录表里要存学生的学号和队伍 ID而不是直接存用户 ID因为一个学生可能同时参加多个竞赛用学号做业务主键更符合教务处的习惯。字段类型上竞赛的报名开始时间和结束时间用DateTimeField状态字段用CharField加choices参数比如status models.CharField(max_length10, choices[(draft, 草稿), (open, 报名中), (closed, 已截止)])。这样在 admin 后台和接口返回里都能直接显示中文省得前端再写映射。3. 后端接口开发用 DRF 把增删改查写成可复用的视图3.1 序列化器与视图集的最小闭环Django REST framework 的核心就两样东西序列化器和视图集。序列化器负责模型和 JSON 之间的转换视图集负责把增删改查打包成一套标准接口。以竞赛表为例先写序列化器from rest_framework import serializers from .models import Competition class CompetitionSerializer(serializers.ModelSerializer): # 把状态字段的中文显示也返回给前端 status_display serializers.CharField(sourceget_status_display, read_onlyTrue) class Meta: model Competition fields [id, name, description, start_time, end_time, status, status_display, created_at] read_only_fields [created_at]status_display这个字段是 DRF 的一个小技巧sourceget_status_display会自动调用 Django 模型里choices生成的显示方法前端拿到status_display就能直接渲染中文不用自己维护一份映射表。视图集更简单from rest_framework import viewsets from .models import Competition from .serializers import CompetitionSerializer class CompetitionViewSet(viewsets.ModelViewSet): queryset Competition.objects.all().order_by(-created_at) serializer_class CompetitionSerializer # 按状态筛选前端传 ?statusopen 就能过滤 filterset_fields [status]ModelViewSet一次性提供了 list、create、retrieve、update、destroy 五个动作路由里用DefaultRouter注册一下/api/competitions/这组接口就全有了。filterset_fields需要配合django-filter使用装完在settings.py的DEFAULT_FILTER_BACKENDS里配一下就行。3.2 权限控制三种角色看到的数据不一样学科竞赛管理系统里权限是绕不开的。学生只能看报名中的竞赛和自己报名的记录教师能看自己负责的赛道和待评审的作品管理员什么都能看。DRF 的权限类可以自定义from rest_framework import permissions class IsAdminOrReadOnly(permissions.BasePermission): def has_permission(self, request, view): # 安全方法GET/HEAD/OPTIONS所有人可访问 if request.method in permissions.SAFE_METHODS: return True # 写操作只有管理员可以 return request.user.is_authenticated and request.user.role admin然后在视图集里挂上permission_classes [IsAdminOrReadOnly]。更细粒度的控制可以重写get_queryset比如学生登录后只返回自己学院的竞赛def get_queryset(self): user self.request.user if user.role student: return Competition.objects.filter(collegeuser.college) return Competition.objects.all()这种写法比在权限类里判断更灵活因为查询集过滤是在数据库层面完成的不会把无权数据查出来再丢掉。3.3 报名接口的事务处理防止超报和重复报名报名这个动作看着简单其实有两个并发问题一是名额满了还能报进去二是同一个学生重复提交。解决办法是用数据库事务加唯一约束。先给报名记录表加一个联合唯一索引class Registration(models.Model): student models.ForeignKey(User, on_deletemodels.CASCADE) competition models.ForeignKey(Competition, on_deletemodels.CASCADE) team models.ForeignKey(Team, on_deletemodels.CASCADE, nullTrue) class Meta: unique_together [student, competition]然后在视图里用transaction.atomic包住报名逻辑from django.db import transaction transaction.atomic def register(self, request, pkNone): competition Competition.objects.select_for_update().get(pkpk) if competition.registered_count competition.max_count: return Response({error: 名额已满}, status400) # 创建报名记录唯一约束会拦住重复报名 Registration.objects.create(studentrequest.user, competitioncompetition) competition.registered_count 1 competition.save() return Response({msg: 报名成功})select_for_update()会在数据库层面加行锁防止两个请求同时读到相同的registered_count。唯一约束则是最后一道防线即使锁没拦住数据库也会报错前端收到错误提示就行。这两个机制配合使用基本能覆盖毕设演示的所有场景。4. 前端 Vue 工程从环境配置到接口联调4.1 Vue 环境搭建与项目初始化前端这块Vue 2 和 Vue 3 都行但新项目建议直接上 Vue 3 Vite启动快、配置少。装环境的第一步是装 Node.js去官网下 LTS 版本装完在命令行验证node -v npm -v两个命令都能输出版本号就说明装好了。然后创建项目npm create vitelatest competition-frontend -- --template vue cd competition-frontend npm install npm install axios vue-router pinia element-plus这里装了四个包axios 发请求、vue-router 管路由、pinia 管状态、element-plus 提供 UI 组件。element-plus 的表格和表单组件能省掉大量样式代码对毕设来说性价比很高。4.2 路由设计与登录拦截学科竞赛管理系统的前端页面大概分三类公开页登录、注册、竞赛列表、学生页我的报名、队伍管理、管理页竞赛管理、评审管理。路由用meta字段标记是否需要登录和角色const routes [ { path: /login, component: Login }, { path: /competitions, component: CompetitionList }, { path: /admin, component: AdminLayout, meta: { requiresAuth: true, role: admin }, children: [ { path: competitions, component: CompetitionManage }, { path: reviews, component: ReviewManage } ] } ]然后在router.beforeEach里做拦截router.beforeEach((to, from, next) { const token localStorage.getItem(token) const role localStorage.getItem(role) if (to.meta.requiresAuth !token) { next(/login) } else if (to.meta.role to.meta.role ! role) { next(/competitions) } else { next() } })这段逻辑的意思是需要登录的页面没令牌就跳登录页角色不匹配就跳回竞赛列表。令牌和角色存在 localStorage 里登录成功后写入退出时清空。4.3 axios 封装与跨域处理接口调用统一走一个封装好的 axios 实例好处是能自动带令牌、统一处理错误import axios from axios const request axios.create({ baseURL: http://127.0.0.1:8000/api, timeout: 5000 }) request.interceptors.request.use(config { const token localStorage.getItem(token) if (token) { config.headers.Authorization Bearer ${token} } return config }) request.interceptors.response.use( response response.data, error { if (error.response error.response.status 401) { localStorage.clear() window.location.href /login } return Promise.reject(error) } ) export default request跨域问题在后端解决更省事装django-cors-headers后在settings.py里配INSTALLED_APPS [..., corsheaders] MIDDLEWARE [corsheaders.middleware.CorsMiddleware, ...] CORS_ALLOWED_ORIGINS [http://localhost:5173]CORS_ALLOWED_ORIGINS里填前端启动的地址Vite 默认是 5173 端口。注意这个中间件要放在CommonMiddleware之前否则不生效。5. 避坑与排查那些让我熬夜的报错5.1 mysqlclient 装不上报 Visual C 错误现象pip install mysqlclient在 Windows 上直接报错提示缺少 Microsoft Visual C 14.0。原因mysqlclient 是 C 扩展需要本地编译Windows 默认没有编译环境。解决两个办法。一是去微软官网下 Visual Studio Build Tools勾选「C 生成工具」装上二是改用 pymysql在项目__init__.py里加import pymysql pymysql.install_as_MySQLdb()然后在settings.py里把ENGINE改成django.db.backends.mysql不变pymysql 会伪装成 MySQLdb。这个办法更轻量推荐毕设用。5.2 前端请求后端报 CORS 错误现象浏览器控制台报Access to XMLHttpRequest has been blocked by CORS policy。原因前端 5173 端口和后端 8000 端口不同源浏览器默认拦截跨域请求。解决确认django-cors-headers已装且中间件顺序正确CORS_ALLOWED_ORIGINS里填的是完整地址带协议和端口比如http://localhost:5173不能只写localhost:5173。改完重启 Django 服务。5.3 中文存进数据库变成问号现象竞赛名称在 admin 后台显示正常但通过接口返回给前端就变成???。原因数据库连接字符集不是 utf8mb4或者建库时用了默认的 latin1。解决先检查建库语句确认是CHARACTER SET utf8mb4。再检查settings.py的OPTIONS里有没有charset: utf8mb4。两个都对了还不行就在 MySQL 命令行执行SHOW VARIABLES LIKE character%看character_set_server是不是 utf8mb4不是的话改 MySQL 配置文件my.ini里的character-set-serverutf8mb4重启 MySQL 服务。5.4 报名接口并发测试时超报现象用压测工具同时发 10 个报名请求名额只有 5 个结果 10 个都成功了。原因没用事务和行锁多个请求同时读到相同的registered_count。解决按 3.3 节的写法用transaction.atomic加select_for_update()。注意select_for_update()必须在事务里才生效单独用没用。另外 MySQL 的存储引擎必须是 InnoDBMyISAM 不支持行锁。5.5 Vue 打包后刷新页面 404现象npm run build之后把 dist 扔给 Nginx首页能打开但刷新/admin/competitions就 404。原因前端路由是 history 模式Nginx 找不到对应的物理文件。解决在 Nginx 配置里加try_files $uri $uri/ /index.html;把所有找不到的路径都指回 index.html让 Vue Router 接管。或者把路由模式改成 hash 模式URL 里带#但不太好看。6. 论文写作与答辩演示的取巧技巧6.1 论文里怎么把 CRUD 写出技术深度毕设论文最怕写成操作手册通篇「点击新增按钮输入竞赛名称」。想让论文有技术含量得在几个地方做文章。数据库设计章节不要只贴建表语句要画 E-R 图然后解释为什么报名记录表要用联合唯一索引、为什么成绩表要冗余存队伍名称。接口设计章节不要只列 URL要讲清楚为什么用 DRF 的视图集而不是函数视图、JWT 令牌的过期时间怎么定、权限类是怎么拦截越权请求的。我一般会在论文里加一节「性能优化」把select_related和prefetch_related的用法写进去再配一个对比表格优化前查竞赛列表要执行 N1 条 SQL优化后只要 2 条。这种有数据支撑的细节导师一看就知道是真做过。6.2 答辩演示的脚本设计答辩演示最忌讳现场翻车所以脚本要提前写好每一步点哪里、说什么都固定下来。我的习惯是准备两个账号一个管理员、一个学生。演示流程按这个顺序走步骤操作讲解要点1管理员登录进入后台展示角色区分和权限控制2新建一个竞赛设置报名时间展示表单验证和状态流转3切换到学生账号报名该竞赛展示跨角色数据隔离4管理员审核报名录入成绩展示完整业务闭环5打开数据库客户端展示表数据证明数据真实落库每一步控制在 30 秒以内总时长不超过 5 分钟。演示前把数据库重置到初始状态避免脏数据导致意外。6.3 源码和数据库的交付习惯最后说一个容易被忽略的点交付。毕设提交的材料里源码和数据库脚本是分开的。数据库脚本用mysqldump导出mysqldump -u root -p competition_db competition_db.sql导出的 SQL 文件里要包含建库语句和初始数据这样导师拿到之后直接source一下就能跑。源码里记得把settings.py里的数据库密码换成占位符再附一个README.md写清楚安装步骤。我吃过亏有一次把带密码的配置直接交上去被导师说了一顿。后来就养成了习惯交付前全局搜一遍password确认没有明文泄露。这套方案我从头到尾跑过不止一遍每次都有新坑但每次填完坑系统就更稳一点。希望帮到你。本文还有配套的精品资源点击获取
阅读完成 · 觉得有帮助?
咨询建站