Django+Vue+MySQL图书馆管理系统毕设全栈实战指南
发布时间:2026/10/7 3:10:15 作者:尧图编辑部 阅读量:1,286

简介面向图书馆管理场景的毕业设计资源基于PythonDjangoVueMySQL技术栈开发采用B/S架构覆盖管理员与用户两类角色。管理员端实现图书分类、图书信息维护、借阅与归还登记、罚金缴纳、留言板及系统管理等模块用户端包含个人中心、借阅/归还记录、罚金查询、收藏管理前台首页整合图书信息、公告、留言反馈和后台入口业务链路完整。压缩包约65.38MB适合计算机相关专业学生用于毕业设计、课程设计参考或二次开发。目前已有320人学习/浏览。配套内容涵盖项目源码、数据库脚本、毕业论文、答辩PPT及视频演示可帮助读者快速复现系统理解Django后端接口、Vue前端交互与MySQL表结构设计节省从零搭建项目的时间。1. 图书馆管理系统毕设为什么我劝你把 DjangoVueMySQL 这套组合做完图书馆管理系统是毕业设计里最经典的“全栈练习生”题目标题里的技术栈已经写得很明确Python Django Vue MySQL配套交付物是源码、数据库、论文答辩、毕业论文和视频演示。我的判断是这个组合放在今天依然值得做但你要做的不是“写一个图书借还页面”而是借这个题目把 Django 的 ORM、DRF 接口、Vue 的路由与组件通信、MySQL 的表设计和索引全部串起来。它能在答辩现场被评委连续追问十分钟不露馅也能在同学真用起来的时候不出大错。适合两类人一类是选题已经定死、准备走完整流程拿论文和答辩材料的学生另一类是借管理系统题目把前后端分离开发摸透的新手。这篇文章我不复制任何现成源码给你而是把一个能正常跑完的方案拆开讲环境怎么搭、目录怎么建、模型怎么设计、接口怎么出、前端怎么接、坑在哪里、论文和答辩材料怎么配齐。2. 技术选型与项目骨架先把环境、版本和目录定下来2.1 为什么是 DjangoVueMySQL这套选型的逻辑和边界很多人在选题阶段纠结过另一个选项SpringBoot Vue MySQL。我的观点是如果只求稳、求快、求答辩能讲明白Django 这套更省心。Django 自带 Admin 后台和 ORM意味着你在做完接口之前就已经有了一个能录入图书数据的后台页面这对论文里的“系统实现”部分非常占便宜Vue 负责前端页面组件化的写法比 jQuery 时代的拼字符串好维护得多MySQL 则是教程最多、安装最容易的数据库你遇到的问题基本都能搜到答案。但也要说清楚这套组合的边界。如果你的项目只是“图书增删改查”那其实不需要前后端分离Django 模板就够了上 Vue 唯一的意义是迎合题目要求如果要做高并发、多租户、微服务这套组合会非常吃力。它的舒适区恰好是“单机可跑、结构完整、扩展点清晰”的毕业设计——既能展示你有前端工程化意识又不会把工作量堆到一个人做不完。2.2 Windows 本地环境的版本组合与安装顺序毕业设计绝大多数是在 Windows 上跑的。版本组合我建议按这张表来定不要贪新组件推荐版本说明Python3.8 / 3.9 / 3.10别上 3.12部分库的 wheel 还不全Django4.2 LTS长期支持版本教程多兼容性好MySQL5.7 或 8.05.7 教程最多8.0 注意认证插件差异VueVue 2 Element UI毕设模板和网课资源最丰富Node.js16.x 或 18.xVue CLI 5 在 Node 18 下正常先说安装顺序Python → MySQL → Node.js → 创建虚拟环境。Python 安装时一定要勾选 “Add Python to PATH”这是最常见的翻车点MySQL 安装时选择 Server only记住你设置的 root 密码字符集后面单独讲。装完 Python 后我习惯先建虚拟环境再装依赖。打开终端进入你的项目目录mkdir library_project cd library_project python -m venv venv venv\Scripts\activate pip install django djangorestframework django-cors-headers pymysql这里的python -m venv venv是在当前目录创建一个虚拟环境激活后终端前面会出现(venv)前缀django-cors-headers是给 Vue 开发服务器跨域调用接口用的pymysql是让 Django 以纯 Python 方式连接 MySQL 的驱动。如果你用的是 MySQL 8.0建议顺手把cryptography也装上否则连接时会报认证相关错误。2.3 用 Django 命令把项目骨架立起来环境和依赖准备好之后下一步是创建 Django 项目和应用。常见做法是一个后端项目配三个业务 appbooks管图书、users管读者、borrow管借阅记录。django-admin startproject library_server . python manage.py startapp books python manage.py startapp users python manage.py startapp borrow命令执行完你的项目目录长这样根目录下有manage.py、library_server配置包以及三个业务 app 文件夹。注意我特意在startproject后面加了一个点意思是“在当前目录生成项目文件”不加这个点它会多套一层目录后面所有manage.py命令都会多跑一层新手很容易在这里绕晕。接下来打开library_server/settings.py把 app 注册进去同时配置 MySQL 连接。这一步是很多人第一次接触 Django 与 MySQL 对接的地方INSTALLED_APPS [ django.contrib.admin, django.contrib.auth, django.contrib.contenttypes, django.contrib.sessions, django.contrib.messages, django.contrib.staticfiles, rest_framework, corsheaders, books, users, borrow, ] MIDDLEWARE [ corsheaders.middleware.CorsMiddleware, django.middleware.security.SecurityMiddleware, # ... 其他默认中间件保留 ] DATABASES { default: { ENGINE: django.db.backends.mysql, NAME: library_db, USER: root, PASSWORD: 你的数据库密码, HOST: 127.0.0.1, PORT: 3306, } }这里的关键点是CorsMiddleware要放在CommonMiddleware前面这是官方文档明确要求的位置放错了跨域配置会不生效NAME对应的数据库需要你先在 MySQL 里手动创建Django 不会自动建库。字符集的问题我放在第 5 章讲但你如果希望后面少走弯路建库时直接指定utf8mb4。3. Django 后端实现模型设计、DRF 接口与 JWT 登录的落地路径3.1 数据模型怎么写图书、读者、借阅记录三张表这个题目的核心业务是图书借还所以数据模型不需要设计得很复杂但三张表的关系必须清晰图书表Book、读者表这里直接用 Django 自带的User扩展、借阅记录表BorrowRecord。有些模板会加一个Category分类表我认为可以加因为答辩时分类查询是个很好的演示点。books/models.py里我一般这样写from django.db import models class Category(models.Model): name models.CharField(max_length50, uniqueTrue) def __str__(self): return self.name class Meta: verbose_name_plural 图书分类 class Book(models.Model): title models.CharField(max_length200, db_indexTrue) author models.CharField(max_length100) isbn models.CharField(max_length20, uniqueTrue) category models.ForeignKey(Category, on_deletemodels.SET_NULL, nullTrue) total_copies models.IntegerField(default1) available_copies models.IntegerField(default1) publish_date models.DateField(nullTrue, blankTrue) created_at models.DateTimeField(auto_now_addTrue) def __str__(self): return self.title几个参数值得展开说。db_indexTrue给title加了索引图书列表页按书名搜索时性能会好一点虽然数据量小感觉不明显但论文里写“对高频查询字段建立了索引”是一个加分项。on_deletemodels.SET_NULL表示删除分类时保留图书只是把category置空如果你用CASCADE删一个分类会把整个分类下的所有图书都删掉答辩时被问到“删分类会不会误删图书”这个回答会让评委觉得你考虑过数据安全。isbn加uniqueTrue防止重复录入同一本书。borrow/models.py里的借阅记录是业务核心from django.db import models from django.conf import settings from books.models import Book class BorrowRecord(models.Model): STATUS_CHOICES [ (borrowed, 借出中), (returned, 已归还), (overdue, 逾期未还), ] user models.ForeignKey(settings.AUTH_USER_MODEL, on_deletemodels.CASCADE) book models.ForeignKey(Book, on_deletemodels.CASCADE) borrow_date models.DateField() due_date models.DateField() return_date models.DateField(nullTrue, blankTrue) status models.CharField(max_length10, choicesSTATUS_CHOICES, defaultborrowed) class Meta: ordering [-borrow_date] def __str__(self): return f{self.user} - {self.book} - {self.status}due_date是应还日期return_date是实际归还日期两者之差就是逾期天数。我没有把逾期天数做成一个字段而是在视图里动态计算这样避免每次归还都要更新两个字段。Status用CharField加choices而不是建一张状态表因为状态是固定枚举建表反而过度设计。这里要专门提醒一下 Django 的删除语义BorrowRecord.objects.filter(userxxx).delete()是批量删除返回的是删除条数record.delete()只删一条。你如果在视图里混用这两种写法会出现“想删一条结果把历史记录全清空”的事故答辩演示时尤其要小心。3.2 DRF 接口用 ViewSet 和 Serializer 快速暴露 CRUD前后端分离的毕设接口层用 Django REST Framework 是最常规的。它最方便的地方是ModelViewSet能自动生成列表、详情、新增、更新、删除五个接口你不用为每个操作单独写函数视图。books/serializers.pyfrom rest_framework import serializers from .models import Book, Category class CategorySerializer(serializers.ModelSerializer): class Meta: model Category fields [id, name] class BookSerializer(serializers.ModelSerializer): category_name serializers.CharField(sourcecategory.name, read_onlyTrue) class Meta: model Book fields [id, title, author, isbn, category, category_name, total_copies, available_copies, publish_date]category_name是只读字段作用是前端列表页直接显示分类名不用再额外发一次请求查分类表。序列化器里source参数指定了这个字段从哪里取值这是 DRF 里很实用的技巧。books/views.pyfrom rest_framework import viewsets from .models import Book from .serializers import BookSerializer class BookViewSet(viewsets.ModelViewSet): queryset Book.objects.all() serializer_class BookSerializer然后在library_server/urls.py里注册路由from django.contrib import admin from django.urls import path, include from rest_framework.routers import DefaultRouter from books.views import BookViewSet router DefaultRouter() router.register(rbooks, BookViewSet) urlpatterns [ path(admin/, admin.site.urls), path(api/, include(router.urls)), ]到这里访问http://127.0.0.1:8000/api/books/就能看到图书列表的 JSON 数据了而且是同时支持 GET、POST、PUT、DELETE 的。DefaultRouter会自动注册GET /api/books/和GET /api/books/{id}/两条路由前者是列表后者是详情。这就是 DRF 的价值一个项目里 80% 的接口都是对单张表的增删改查ViewSet 帮你把样板代码压缩到三行。分页配置我给一个建议值放在settings.py里REST_FRAMEWORK { DEFAULT_PAGINATION_CLASS: rest_framework.pagination.PageNumberPagination, PAGE_SIZE: 10, }3.3 JWT 登录与跨域配置答辩时被追问的三个点登录认证是论文里“系统安全性”章节的主要素材。毕设场景下最合适的方案是 JWT而不是 Django 自带的 Session因为前端是 Vue 单页应用JWT 天然适合这种无状态接口调用方式。安装并配置djangorestframework-simplejwtpip install djangorestframework-simplejwt然后在settings.py里加上认证配置REST_FRAMEWORK { DEFAULT_AUTHENTICATION_CLASSES: ( rest_framework_simplejwt.authentication.JWTAuthentication, ), }urls.py里加两个认证接口from rest_framework_simplejwt.views import TokenObtainPairView, TokenRefreshView urlpatterns [ path(api/auth/login/, TokenObtainPairView.as_view()), path(api/auth/refresh/, TokenRefreshView.as_view()), ]JWT 的核心机制是用户登录后后端返回access和refresh两个 token前端把access存在 localStorage 里每次请求在请求头加Authorization: Bearer tokenaccess过期后用refresh换新的。答辩时如果被问“为什么不用 Session 而用 JWT”你可以答前后端分离部署时Session 依赖 Cookie 同源策略跨域场景下处理麻烦JWT 是无状态的后端不用存会话扩展性更好。跨域配置则是另一个必踩的点。Vue 开发服务器跑在http://localhost:8080Django 跑在http://localhost:8000端口不同就是跨域。用django-cors-headers解决CORS_ALLOWED_ORIGINS [ http://localhost:8080, http://127.0.0.1:8080, ]我只建议在开发阶段放开这两个地址不要用CORS_ALLOW_ALL_ORIGINS True那个设置等于直接告诉所有网站“你们都能调用我”毕业论文写这段话会被问得很尴尬。4. Vue 前端实现路由、axios 封装与图书借阅页面的组件化4.1 用 Vue CLI 创建前端项目并配置代理转发前端这里以 Vue 2 Element UI 为例因为毕业设计的主流资源都在这套组合上。先把 Node 环境准备好然后装 Vue CLInpm install -g vue/cli vue create library_webvue create会让你选预设建议选 “Manually select features”勾上 Router 和 VuexVue version 选 2.x。生成完项目后进入目录装 Element UIcd library_web npm install element-ui axios装好后在main.js里全局注册 Element UIimport Vue from vue import ElementUI from element-ui import element-ui/lib/theme-chalk/index.css import App from ./App.vue Vue.use(ElementUI) new Vue({ render: h h(App) }).$mount(#app)然后处理跨域问题。开发阶段最省事的方式不是上面第 3 章里配的 CORS而是用 Vue CLI 的代理让 Vue 开发服务器把/api开头的请求转发到 Django 的 8000 端口。在项目根目录创建vue.config.jsmodule.exports { devServer: { port: 8080, proxy: { /api: { target: http://127.0.0.1:8000, changeOrigin: true } } } }这样你在前端请求/api/books/时浏览器看到的是同源的localhost:8080实际请求被代理转发到localhost:8000。开发阶段用代理部署阶段才考虑 CORS这是前后端分离项目的常规实践。和“把 Vue 打包塞进 SpringBoot”那套单应用部署不同毕设里更推荐保持前后端两个服务器独立跑演示时两个终端窗口同时开反而更直观。4.2 路由与页面骨架登录页、图书列表页、借阅记录页用 Vue Router 管理页面跳转。先装依赖npm install vue-router3然后创建src/router/index.jsimport Vue from vue import Router from vue-router import Login from ../views/Login.vue import BookList from ../views/BookList.vue import BorrowRecord from ../views/BorrowRecord.vue Vue.use(Router) const router new Router({ routes: [ { path: /login, component: Login }, { path: /books, component: BookList, meta: { requiresAuth: true } }, { path: /records, component: BorrowRecord, meta: { requiresAuth: true } }, { path: /, redirect: /books } ] }) router.beforeEach((to, from, next) { const token localStorage.getItem(token) if (to.meta.requiresAuth !token) { next(/login) } else { next() } }) export default router路由守卫是这一段的核心。beforeEach在每次路由跳转前执行如果目标页面标记了requiresAuth: true而本地没有 token就强制跳回登录页。这个逻辑就是论文里写的“前端路由级访问控制”。页面文件按惯例放在src/views/目录下组件放src/components/。图书列表页用表格组件展示数据加一个搜索框和一个“借阅”按钮这是整个系统的主界面。4.3 axios 封装与图书列表页的组件化实现直接在每个组件里写fetch会非常零散我习惯先封装一个 axios 实例把 token 注入和 401 处理都放在这一层。src/utils/request.jsimport axios from axios import router from ../router const service axios.create({ baseURL: /api, timeout: 10000 }) service.interceptors.request.use(config { const token localStorage.getItem(token) if (token) { config.headers.Authorization Bearer token } return config }) service.interceptors.response.use( response response, error { if (error.response.status 401) { localStorage.removeItem(token) router.push(/login) } return Promise.reject(error) } ) export default service请求拦截器做了一件事把 localStorage 里的 token 拼成Authorization头加到每个请求上。响应拦截器处理 401token 过期时清掉并跳回登录页。这套封装让你的业务页面里完全不用关心 token 怎么传、过期了怎么办。图书列表页的BookList.vue核心片段长这样template el-table :databooks stripe el-table-column proptitle label书名/el-table-column el-table-column propauthor label作者/el-table-column el-table-column propcategory_name label分类/el-table-column el-table-column propavailable_copies label可借数量/el-table-column el-table-column label操作 width120 template slot-scopescope el-button typeprimary sizesmall clickborrowBook(scope.row)借阅/el-button /template /el-table-column /el-table /template script import request from ../utils/request export default { data() { return { books: [] } }, created() { this.fetchBooks() }, methods: { async fetchBooks() { const res await request.get(/books/) this.books res.data.results }, async borrowBook(row) { const res await request.post(/borrow/, { book_id: row.id }) if (res.status 201) { this.$message.success(借阅成功) this.fetchBooks() } } } } /script这里有一个细节el-table里我用了category_name而不是category因为后端序列化器里已经把这个字段拼出来了。前后的字段名保持一致是前后端分工时最需要提前约定的事情。借阅按钮调用的POST /borrow/对应第 3 章里没有展开的BorrowViewSet完整的项目里还要加对user和due_date的赋值比如借阅时在前端不传这两个字段由后端从 JWT 里拿用户、从当前日期计算应还日期。前端项目做完后如果要把源码发给同学或者传给老师看我建议写一个README.md把“装依赖、改数据库密码、跑后端、跑前端”四步写清楚。很多人收到现成源码第一件事就是npm install然后报错错在数据库连接不上——不是你代码的问题是你没告诉对方要改密码。5. 避坑排查毕业设计容易翻车的 5 个现场5.1 现象MySQL 里的中文全是问号生僻字直接丢失这是毕设项目里出现频率最高的问题。很多人建表时用了默认字符集MySQL 在 Windows 上默认可能是latin1中文自然存不进去。更隐蔽的情况是中文能显示但 emoji 或生僻字存进去就变问号这是因为utf8字符集存不下四字节的字符。解决方案建库时直接指定utf8mb4它兼容所有 Unicode 字符。CREATE DATABASE library_db DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci;同时确认 Django 配置里没有额外指定别的字符集Django 4.x 默认就能识别 MySQL 的utf8mb4。如果你的库已经建好了可以用ALTER DATABASE library_db CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci;转换已经存进去的乱码数据不会自动恢复所以最好是项目一开始就定好。5.2 现象配了 django-cors-headers前端还是报跨域我见过不少同学按教程装了这个库、加了CORS_ALLOW_ALL_ORIGINS True浏览器控制台依然报 CORS 错误。原因是中间件顺序不对。前面第 2 章里我特意强调CorsMiddleware要放在CommonMiddleware之前这就是这个问题的根源——放反了请求在到达 CORS 中间件之前就被处理了响应头里没有Access-Control-Allow-Origin浏览器就判定跨域失败。解决方式就是调整MIDDLEWARE里的顺序corsheaders.middleware.CorsMiddleware放最前面。另外要注意如果CORS_ALLOWED_ORIGINS里写的是http://localhost:8080但你在浏览器访问的是127.0.0.1:8080同样算跨域两个都要加。5.3 现象python manage.py migrate 报错连不上 MySQL 或者驱动找不到常见的报错有两个。一个是ModuleNotFoundError: No module named MySQLdb这是 Django 默认用 MySQLdb 驱动但你没装mysqlclient。如果你用的是pymysql需要在项目的__init__.py里写一段兼容代码import pymysql pymysql.install_as_MySQLdb()另一个报错是Access denied for user rootlocalhost通常是密码错误或者 MySQL 8.0 的认证插件是caching_sha2_passwordpymysql 旧版本不支持。解决方式升级pymysql到最新版或者用pip install cryptography补上加密库。5.4 现象Django 里删除数据时一条成功、多条报错或者一删删一片这是 ORM 使用方法的问题。record BorrowRecord.objects.get(id1); record.delete()删除单条BorrowRecord.objects.filter(useruser).delete()是批量删除返回的是(总删除条数, {模型名: 数量})这样的元组。新手最常见的错误是在循环里先filter再取.first()如果没加first()直接用filter()调用.delete()就是批量删除一个疏忽把整个用户的历史借阅记录清空了。更危险的是外键联动删除。Book上如果外键设置了on_deletemodels.CASCADE删除一本书会把所有关联的借阅记录一起删掉。在设计阶段别偷懒借阅记录表是业务流水一旦被级联删除论文里的“数据完整性”就没法讲了。建议BorrowRecord里关联Book的外键改成on_deletemodels.PROTECT这样有人借过这本书时系统会禁止删除这也是现实业务该有的逻辑。5.5 现象前端登录成功刷新页面后又跳回登录页原因几乎只有一个登录成功后你把 token 存到了 Vuex 的 state 里刷新页面 Vuex 状态重置token 丢了。解决方式很简单登录成功后把 token 持久化到localStorageaxios 拦截器每次从 localStorage 里读取。这也就是我在第 4 章第 3 节里封装request.js时加那两段拦截器的原因。毕设里不需要引入vuex-persistedstate这类库一个localStorage足够。如果你刷新后 token 还在但依然跳登录页那要检查router.beforeEach里读 token 的 key 名是不是写错了。有人登录时存的是access_token守卫里读的是token这种低级错误在答辩演示现场会非常尴尬。我建议全项目统一叫token不要混用多个名字。6. 论文、答辩PPT与视频演示的验收节奏把代码变成学分6.1 毕业论文怎么搭用 ER 图讲数据流而不是贴代码论文的核心不是贴代码而是讲清楚“你想了什么”。我建议按这样的章节顺序组织绪论背景和意义→ 需求分析功能需求 非功能需求→ 系统设计架构图 ER 图 接口设计→ 系统实现核心功能截图 关键代码片段→ 系统测试测试用例表 结果分析。评审老师最看重的是第三章系统设计这里你要画出三张核心 ER 图图书表、用户表、借阅记录表之间的关系以及前端页面到后端接口到数据库表的请求链路。论文里写“关键技术”时不要大段抄 Django 官方文档而是写你实际用到的机制ORM 如何映射数据表、JWT 无状态认证的工作流程、Vue 路由守卫的作用。每写一段都问自己“这段能回答评委的什么问题”写出来才有答辩价值。6.2 答辩 PPT 和视频演示5 分钟脚本 三个必演示场景视频演示时长控制在 5 分钟以内录屏脚本我建议固定为登录系统 → 分类和图书管理演示新增一本书→ 借阅流程借书 查看借阅记录→ 归还流程查看归还后库存变化。这三个场景覆盖了系统 90% 的功能也对应了数据库的增删改查四个操作。答辩 PPT 不要超过 10 页逻辑是题目和背景一页、技术选型一页、ER 图和架构图一页、功能演示截图三页、测试结果一页、总结一页。PPT 里提到的每个功能点都得保证现场能演示出来否则老师让你“打开系统看一下”时就露馅了。最后做一个验收自测清单生产环境里我习惯在交付前跑一遍这些场景测试场景预期结果新增图书库存 5可借 5列表页显示可借 5借阅 2 本后可借变 3借阅记录出现 2 条归还 1 本后可借变 4记录状态为已归还删除已被借阅的图书系统拦截提示存在关联记录重复 ISBN 入库被唯一约束拦截报错正常提示错误密码登录返回 401前端跳转登录页token 过期后访问接口返回 401前端自动清理 token我做过不少管理类项目最想强调的一点是演示顺畅不等于项目合格老师追问的逻辑链路往往藏在数据库关系和异常处理里。你如果能在答辩前把“这本书被删了会怎样”“用户逾期了状态怎么变”这类边界问题自己先想清楚整个答辩的气场就稳了。希望这篇笔记能让你的毕设少走几个弯路把时间花在真正加分的地方。本文还有配套的精品资源点击获取