资讯动态

Python+Vue学校实验室管理系统开发指南:Django与Flask选型实践

发布时间:2026/10/20 4:07:28 来源:尧图企业网站定制
从标题说起这类“PythonVue的学校实验室管理系统”几乎是每所高校信息类项目的标配题目。你一看关键词就明白它涉及三个技术栈Python做后端、Vue做前端、PyCharm做开发环境而Django和Flask则是Python生态里真正决定你项目骨架走向的两个Web框架。这篇文章会把我做类似系统时的完整思路、踩过的坑、以及最终形成的一套可复用方案全部讲透。先亮明观点在时间和精力有限的前提下我建议学校实验室管理系统这类偏传统管理后台的项目直接选Django Django REST Framework做后端前端用Vue 3 Element Plus开发工具用PyCharm Professional。Flask不是不行但它的优势在轻量灵活用在课程设计或原型阶段很爽一旦面临权限、审批流、统计报表这些规范化需求Django自带的后台和ORM能省下大量重复工作。下面我会从设计思路、技术选型、数据库建模、前后端实现路径到常见坑一条线全部拆开讲。1. 项目概述实验室管理系统到底在解决什么问题1.1 学校实验室管理的真实痛点实验室管理如果没有系统支撑几乎一定乱在三个地方。第一设备台账靠Excel。每学期末盘点谁从库里借了示波器、哪台电脑坏了、适配器丢了多少个靠人工对账能对到怀疑人生。部门里通常会有一张“公共共享表格”几十个人同时编辑数据被覆盖、格式被搞乱是家常便饭。第二实验室预约靠口头或微信。排课冲突、空闲时段不透明学生想用实验室不知道哪个时间段有空管理员也不清楚某时段是否被占用最后只能用“先到先得”的土办法。第三借用与归还流程没有留痕。设备借出时刷个脸或签个字归还时长毛了说不清设备坏了谁弄坏的、什么时候坏的没有一个标准记录追责根本无从谈起。这正是“学校实验室管理系统”的核心使命把设备、预约、借用、审批、公告这些散落的数据统一收进一个Web平台让管理员可见可用、学生可查可预约。1.2 系统的目标使用人群这套系统的用户画像非常清晰三个角色管理员负责设备入库、审批借用申请、发布公告、统计设备使用率、处理异常归还。教师可以代替学生发起借用也可以查看自己班级或课题项目关联的设备情况。学生查看空闲实验室、在线预约时段、提交设备借用申请、查询个人借还记录。角色不同操作路径完全分离。这也是为什么系统必须要有权限设计而不是简单的“登录后直接都能用”。1.3 为什么该用Web系统而不是单机程序有些同学会问实验室管理做成PyQt或Tkinter桌面程序行不行技术上完全能做但本质上桌面端绕不开“多端访问”这个坎。今天管理员在办公室登记设备明天他在机房想审批一条申请总不能到处带着程序跑。换成Web系统后端跑在一台服务器上所有浏览器都是客户端手机也能访问这才符合“管理”和“协同”的本质。Vue负责前端交互Python负责接口与数据持久化前后端分离后端换成谁都行这才是一个能长期演化、也更容易拿到高分的系统架构。2. 技术选型Django与Flask到底怎么选Vue在其中扮演什么角色2.1 Django和Flask选择策略标题把Django和Flask都挂了进来实际开发时你只能确定一个主架构或者用一个做主干另一个做某个微服务模块。我的建议很明确如果项目的核心诉求是快速交付、功能完整、权限清晰选Django。如果项目偏向演示原型、高并发不是重点、想最大限度保留手写自由度选Flask。我用一个表格说明区别对比维度DjangoFlask项目结构自带project与app目录规范默认单模块自由组织ORM内置强大ORM支持迁移需要配合SQLAlchemy后台管理Admin后台开箱即用需要自己实现认证体系自带User模型与认证视图靠Flask-Login等扩展学习和开发成本前期需要理解框架约定上手快灵活度高适合场景规范化的管理系统轻量服务、API演示从学校实验室管理系统这种业务形态看Django几乎是为它量身定做的。比如设备表要用到外键关联实验室房间、设备分类借用单要关联用户审批状态要有多状态流转统计报表要聚合查询——Django ORM里这些都是标准操作换个框架你得用不同的插件拼装。反过来说如果我只想三天做出一个“查询实验室空闲状态”的小工具Flask的轻量特性确实更舒服。这部分计划可以根据你的学习节奏来两条路线都能做但别在同一个项目里同时把Django和Flask都当作主体那个架构就比较别扭了。2.2 Django项目的代码组织Django会强制生成一个“项目壳”和若干“功能模块”。实验室管理系统我的典型划分是accounts用户登录、注册学生、教师、管理员三种身份。lab实验室房间信息、排课时间段。equipment设备类型、设备台账、设备借用记录。reservation预约申请与审批流程。notice公告发布与展示。每个app保持低耦合功能就算再复杂也容易排查。有人担心Django的“重量感”实际上就是把这几个app建好再把公共权限逻辑封装成mixins并不臃肿。2.3 Vue在前端的具体价值Vue承担的是“用户能看得见摸得着”的那一层。实验室管理系统的前端页面大概有这些登录页、首页仪表盘、设备列表页、预约申请页、审批页、个人中心、统计分析页。如果不用框架用原生JS操作DOM光是维护这些页面状态就会疯掉。使用Vue后你可以把设备卡片抽象成EquipmentCard组件列表、筛选复用同一组件。用vue-router管理页面路由并用路由守卫拦截未登录用户。用pinia或Vuex管理当前登录用户的token和角色信息。用Axios统一请求把后端返回的数据直接渲染到页面上。Vue本身不解决后端问题但它是把后端数据变成友好界面的关键桥梁。3. 系统整体设计从需求拆分到数据库建模3.1 核心功能模块与页面规划我把这套管理系统的功能拆成六块这是后端设计和前端页面的总蓝图用户管理注册、登录、角色切换、权限校验。实验室信息管理房间名称、容纳人数、开放时间段、当前状态空闲/使用中/维护中。设备管理设备编号、设备名称、所属实验室、设备状态、购置日期、借用人。预约管理学生选择目标实验室和时段提交预约申请管理员/教师审核后生效。借用与归还借用设备时生成记录归还时更新设备状态并记录归还事件。数据统计设备使用率、每周预约次数、热门设备排行。页面侧Vue端需要至少包含/login登录页/dashboard数据概览页/labs实验室列表页/labs/:id实验室详情页含预约入口/equipment设备库页面/my/reservations我的预约列表/admin/approvals管理员审批页/admin/equipment管理员设备管理页3.2 数据库表结构设计后端建模是整个系统的地基。我在设计时坚持一个原则凡是能够用外键关联的绝不使用字符串硬存。这样既保证数据一致性后续统计也不容易出错。核心表如下表名主要字段备注Userusername, password, role, email, student_id继承Django AbstractUser或单独ProfileLabname, location, capacity, status, open_start, open_end实验室房间信息Equipmentname, model, lab_fk, status, borrower_fk, buy_date设备挂到某个实验室下Reservationuser_fk, lab_fk, date, start_time, end_time, status状态有pending/approved/rejectedBorrowRecorduser_fk, equipment_fk, borrow_time, return_time, condition_note借还记录独立成表Noticetitle, content, publish_time公告其中status字段我建议用字符串常量枚举例如设备状态可取值available、borrowed、repairing比布尔值更能表达业务状态。3.3 为什么使用前后端分离而不套用Django模板你说标题里同时出现了Python和Vue那我默认你倾向的是前后端分离用Django只提供RESTful API页面渲染全部交给Vue。这个选择的直接好处有两个一是后端开发与前端开发可以完全并行。你负责写APIUI组用mock数据先把页面搭出来最后联调。二是重构和迁移方便。如果以后要把前端换成小程序后端接口可以原样复用不用动Python代码。而如果借Django模板页面重写时后端模板也要一起推翻。当然前后端分离也带来一个代价必须处理CORS跨域、接口鉴权、前端路由守卫等额外内容。这些我在第5和第6部分细讲。4. 后端落地使用PyCharm从零搭建Django项目4.1 PyCharm中初始化项目与虚拟环境我习惯在PyCharm里直接用虚拟环境新建Django项目过程三步PyCharm选“New Project”左边选Django配置好项目路径。选择虚拟环境Python解释器建议用3.10以上版本。创建后检查settings.py里有没有生成INSTALLED_APPS列表、urls.py、manage.py这些基础文件。如果你的PyCharm是社区版新建项目模板里没有Django选项不用慌直接在终端先装好依赖再手动创建Django项目也行。命令如下pip install django djangorestframework django-admin startproject lab_system cd lab_system python manage.py startapp accounts python manage.py startapp equipment python manage.py startapp lab python manage.py startapp reservation我特别建议在一开始就把app按功能模块拆好不要图省事把所有模型堆在同一个app里否则后边路由和序列化器会互相打架。注意PyCharm Professional对前端支持更好可以直接在IDE内写Vue代码、跑npm脚本Community版虽然也能写但需要大量依赖插件所以我个人倾向开发这套系统时至少用上专业版或者退而求其次用VS Code写前端PyCharm只管Python后端。4.2 定义模型与数据迁移设备模型的写法直接决定查询复杂度我以Equipment为例给出一个可复用的模板from django.db import models from django.contrib.auth import get_user_model User get_user_model() class Equipment(models.Model): STATUS_CHOICES [ (available, 可用), (borrowed, 已借出), (repairing, 维修中), ] name models.CharField(设备名称, max_length100) model models.CharField(设备型号, max_length100) lab models.ForeignKey(lab.Lab, on_deletemodels.PROTECT, verbose_name所属实验室) status models.CharField(设备状态, max_length20, choicesSTATUS_CHOICES, defaultavailable) borrower models.ForeignKey(User, nullTrue, blankTrue, on_deletemodels.SET_NULL, verbose_name当前借用人) buy_date models.DateField(购置日期, nullTrue, blankTrue) created_at models.DateTimeField(auto_now_addTrue) class Meta: db_table equipment ordering [-created_at] def __str__(self): return f{self.name} ({self.model})这里有两个设计细节值得讲on_deletemodels.PROTECT保护外键。如果设备还挂在某个实验室下面就不允许直接删除该实验室从数据安全角度看更稳。borrower用SET_NULL用户注销时不会连带删除历史记录保留借还台账的完整性。定义完成后执行迁移python manage.py makemigrations python manage.py migrate4.3 使用Django REST Framework快速生成API实验室管理系统的API层我不会写大量重复的增删改查逻辑而是直接依赖ModelViewSet加DefaultRouter。下面代码展示设备API的实现from rest_framework import viewsets from rest_framework.permissions import IsAuthenticated from .models import Equipment from .serializers import EquipmentSerializer class EquipmentViewSet(viewsets.ModelViewSet): queryset Equipment.objects.all() serializer_class EquipmentSerializer permission_classes [IsAuthenticated]然后配置路由from rest_framework.routers import DefaultRouter from equipment.views import EquipmentViewSet router DefaultRouter() router.register(equipment, EquipmentViewSet, basenameequipment) urlpatterns router.urls别看代码量少ViewSet已经把list/create/retrieve/update/delete全部实现好了前端调接口直接用标准REST风格。同时我配合序列化器做输入校验from rest_framework import serializers from .models import Equipment class EquipmentSerializer(serializers.ModelSerializer): lab_name serializers.CharField(sourcelab.name, read_onlyTrue) class Meta: model Equipment fields [id, name, model, lab, lab_name, status, borrower, buy_date]这个序列化器会额外返回实验室名称前端列表页就不用再单独发一次实验室detail请求联动查询的压力也减小了。4.4 用户认证与权限控制登录这部分是“管理系统”与普通展示站的分水岭。我采用JWT认证方案配合djangorestframework-simplejwt使用很简单pip install djangorestframework-simplejwt在settings.py里配置from datetime import timedelta SIMPLE_JWT { ACCESS_TOKEN_LIFETIME: timedelta(days1), AUTH_HEADER_TYPES: (Bearer,), }同时给视图类加上自定义的权限类。例如审批操作只允许管理员from rest_framework.permissions import BasePermission class IsAdmin(BasePermission): def has_permission(self, request, view): return request.user.is_authenticated and request.user.role admin这里要注意Django原生的User模型并没有role字段。最简单的做法是自定义一个UserProfile做成OneToOne关联或者直接把User表替换成自定义的。我建议入门阶段用一个Profile模型即可from django.db import models from django.contrib.auth.models import User class Profile(models.Model): user models.OneToOneField(User, on_deletemodels.CASCADE, related_nameprofile) role models.CharField(max_length20, choices( (student, 学生), (teacher, 教师), (admin, 管理员) ), defaultstudent) student_id models.CharField(max_length20, blankTrue, nullTrue)这样你就能在接口里通过request.user.profile.role判断身份前端也能根据角色动态渲染按钮、控制页面元素。5. 前端实现Vue 3 Element Plus搭起管理后台5.1 初始化Vue项目和依赖我推荐用Vite创建Vue 3项目npm create vitelatest lab_front cd lab_front npm install npm install vue-router axios pinia element-plus目录里核心文件就三个src/router/index.js、src/store/user.js、src/App.vue。启动时我用npm run dev默认端口一般是5173。5.2 Axios统一封装与Token注入实验室管理系统的几乎所有请求都要带JWT Token所以Axios必须统一拦截器。在src/utils/request.js中写入import axios from axios import { useUserStore } from ../store/user import router from ../router const request axios.create({ baseURL: http://127.0.0.1:8000/api, timeout: 10000 }) request.interceptors.request.use(config { const store useUserStore() if (store.token) { config.headers.Authorization Bearer ${store.token} } return config }) request.interceptors.response.use( response response, error { if (error.response error.response.status 401) { useUserStore().logout() router.push(/login) } return Promise.reject(error) } ) export default request这样任何API请求只要写一句request.get(/equipment/)Token就会自动带上不用在每个页面手写请求头。5.3 登录页面与状态管理以pinia为例登录逻辑极简import { defineStore } from pinia import request from ../utils/request export const useUserStore defineStore(user, { state: () ({ token: localStorage.getItem(token) || , role: localStorage.getItem(role) || , username: localStorage.getItem(username) || }), actions: { async login(payload) { const res await request.post(/auth/login/, payload) this.token res.data.access this.role res.data.role this.username res.data.username localStorage.setItem(token, this.token) localStorage.setItem(role, this.role) localStorage.setItem(username, this.username) }, logout() { this.token this.role this.username localStorage.clear() } } })路由守卫负责拦截未登录或权限不够的用户router.beforeEach((to) { const store useUserStore() if (!store.token to.path ! /login) { return /login } if (to.path.startsWith(/admin) store.role ! admin) { return /dashboard } })这个小动作的价值非常大。哪怕你后端接口写得再严谨前端先把权限路由拦死一部分整个项目的体验和安全性都会往上走一个档次。5.4 设备列表与借用操作设备列表页是核心展示页。我常用的写法是把表格封装成独立组件配合el-table做数据展示。其中借出操作需要用el-dialog弹窗里面放时间、用途、备注字段调用后端的POST /api/equipment/borrow/接口完成。注意这里不修改设备列表的status就完事还需要生成一条BorrowRecord所以我通常会在Django的视图类中写一个额外动作from rest_framework.decorators import action from rest_framework.response import Response class EquipmentViewSet(viewsets.ModelViewSet): action(detailTrue, methods[post]) def borrow(self, request, pkNone): equipment self.get_object() if equipment.status ! available: return Response({msg: 设备不可借用}, status400) equipment.status borrowed equipment.borrower request.user equipment.save() BorrowRecord.objects.create(equipmentequipment, userrequest.user) return Response({msg: 借用成功})前端调这个接口时只需要request.post(\/equipment/${id}/borrow/)即可。这里想提醒的是不要在列表数据里直接靠前端改status所有状态变更必须走后端API这样记录和历史才能串起来。5.5 管理端审批页面审批流是实验室系统比较考验设计的部分。我的方案是预约状态有pending、approved、rejected三个值。当学生提交预约后端把Reservation记录写成pending管理员在/admin/approvals页面看到所有待审核记录一键通过或驳回。审批接口的实现思路action(detailTrue, methods[post]) def approve(self, request, pkNone): reservation self.get_object() reservation.status approved reservation.save() return Response({msg: 已通过}) action(detailTrue, methods[post]) def reject(self, request, pkNone): reservation self.get_object() reservation.status rejected reservation.reason request.data.get(reason, ) reservation.save() return Response({msg: 已驳回})前端管理列表里每行放“通过”和“驳回”两个按钮按钮上点击后调API并刷新列表。这个流程本质不复杂但需要确保后端在通过前再次校验时间段是否冲突避免多个预约重贴到同一时段。6. 常见问题与排查技巧实录6.1 前后端联调时CORS跨域问题Django后端跑在8000端口Vue跑在5173端口前端直接用Axios请求后端浏览器默认会拦截跨域请求。解决方案有两种第一种是后端加django-cors-headerspip install django-cors-headers在settings中配置INSTALLED_APPS [ ... corsheaders, ] MIDDLEWARE [ corsheaders.middleware.CorsMiddleware, ... ] CORS_ALLOWED_ORIGINS [ http://localhost:5173, ]第二种是生产环境部署时把Vue打包后的静态文件交给Django托管用同一域名访问就不存在跨域。开发时我更推荐第一种省心。6.2 浏览器表单提交老是403CSRF到底怎么处理如果你用DRF的API接口且启用了JWT跨域请求时不再需要CSRF Token。但如果你混合使用了Django的普通表单模板就仍然要处理CSRF。具体到实验室管理系统我“过来人”的经验是凡是走API的接口都统一使用JWT认证相应的SessionAuthentication可以先关闭避免CSRF的校验参入其中。在settings.py中REST_FRAMEWORK { DEFAULT_AUTHENTICATION_CLASSES: [ rest_framework_simplejwt.authentication.JWTAuthentication, ], }注意这一步会让DRF默认的登录页面失效但我们的登录页是自己用Vue实现的完全无影响。6.3 时区与DateTimeField问题Django的USE_TZTrue在保存时间时默认转成UTC如果前端页面显示的时间比本地时间少8小时就是时区没有处理。最简单的做法是把settings.py中TIME_ZONE设为Asia/Shanghai且USE_TZFalse或者继续保留UTC但在前端格式化时统一转本地时间。我个人在管理系统中更喜欢USE_TZFalse因为实验室的时段预约与本地时间强相关数据库存UTC反而要处处转换太绕了。6.4 每次改完模型迁移命令总忘我在开发时几乎每天都会改模型踩过不少因忘记迁移直接导致接口报错的坑。后来习惯用一条命令组合跑python manage.py makemigrations python manage.py migrate如果执行迁移时提示存在冲突或Missing migration别硬来先检查app目录下的migrations文件必要时删除错误的中间迁移记录重新生成。6.5 Vue打包后刷新404前端构建生产包时如果部署到Nginx直接访问/equipment路由会404原因是Vue是SPA刷新时Nginx去找真实文件路径却找不到。解决方案是在Nginx配置中加一条location / { try_files $uri $uri/ /index.html; }这条配置虽小但是部署时十有八九会踩到我建议提前写进部署文档。7. 实操心得与再扩展这一路做下来我最大的感受是实验室管理系统不算什么高深技术但真正考验的是“逻辑完整度”。设备借了总要还预约通过总要占时段审批驳回总要留原因公告发了总要有人看到——每个环节都要能闭环。写代码前建议先画一张“状态流转图”贴在手边哪怕不是正式图也把主流程写出来学生提交预约 - pending - 管理员审批 - approved/rejected学生提交设备借用 - 管理员审核设备状态 - 生成BorrowRecord - 借出归还时 - 更新BorrowRecord的归还时间 - 设备状态恢复available只要这个状态流转不模糊后端的模型字段就不会漏前端的按钮逻辑也不会打架。如果你觉得Django方案还是偏重Flask路线我也实践过另一个版本的实现整体思路一致Flask SQLAlchemy Flask-JWT-Extended配合Vue前端但每张表都需要手写SQLAlchemy模型权限体系也要从头做。在项目周期两周以内的课程设计或毕业课题我还是倾向直接拿Django这套方案理由很清楚它在组织代码、数据库迁移、权限控制和后台管理上都有成熟约定比Flask省下的那些“弯弯绕绕”时间足够你多打磨两版界面或者多做几个统计图表。再往下扩展这个系统还可以加很多实用模块短信或邮件提醒借用到期前自动通知、二维码扫码借用、Excel导入导出设备台账、基于ECharts的可视化大屏。你把这套基础架构搭稳了再往上面加这些功能都是水到渠成的事。最后分享一个小技巧在settings.py里记得把API的PAGE_SIZE配置一下。实验室设备一多前端一次性拿几十上百条数据没问题但到了上千条就会出现明显卡顿。DRF自带分页配上LimitOffsetPagination或PageNumberPagination前端接口响应数据变为{count, results}结构前端列表组件只需要拿到results渲染分页器接到count去显示总页数。别小看这步它就是管理系统从“能跑”变成“好用”的一个关键细节。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑