Django图书馆管理系统:毕业设计可部署的闭环借阅方案 简介本资源是一套面向计算机专业本科生的Python毕业设计完整交付包聚焦图书馆信息化管理场景适用于毕设选题、课程设计与Web全栈开发实践。项目基于Django框架构建集成图书管理、借阅归还、罚款核算等核心业务模块配套论文详述需求分析、UML设计类图/时序图、MySQL数据库实现及单元/系统测试方案。压缩包共783个文件19.21MB含45个Python后端逻辑文件、53个Vue前端组件、164个JS交互脚本、162个SVG图标资源及50余张JPG/PNG界面截图另有安装与运行批处理脚本.bat和说明文档.docx结构清晰、注释完整便于二次开发与功能扩展。目前已有186人学习下载读者可直接部署运行、研读论文写作范式、复用前后端模块代码并基于配置文件快速定制借阅规则或对接校园一卡通等外部系统。1. 这不是又一个“增删改查”Demo它真能跑在校园局域网里管3000册图书、200学生借阅流水且毕业答辩前一周我靠它救回了被导师打回三次的论文框架去年帮某高校信息学院A同学改毕设他交第三版论文时被导师一句“系统没闭环借书流程断在归还环节”直接打回。我们连夜拉出这个Python图书馆管理系统源码包——不是网上抄的Flask小玩具而是带完整Django Admin后台、MySQL事务控制、逾期自动计费逻辑、PDF导出报表的真实可部署系统。它不依赖云服务运行.bat双击就能起服务数据库初始化脚本藏在2-run.bat里连表结构都建好了最关键是借阅-归还-罚款三步状态机写在models.py里用Django信号触发不是if-else硬编码。适合两类人一是正卡在毕设实现层、代码跑不通/功能不闭环的本科生二是想拿现成模块快速搭教学演示系统的指导老师。别被标题里“毕业设计”吓退——它的权限模型、日志埋点、导出接口比很多企业内训项目还扎实。2. 技术栈与工程结构Django 3.2 MySQL 5.7 Bootstrap 46个核心目录撑起全链路这项目不是“Python写个脚本SQLite凑数”它按生产级Django项目规范组织目录结构清晰到能直接当教学模板用。我解压后第一件事就是看manage.py同级的requirements.txt确认版本锁死Django3.2.23LTS版、mysqlclient2.1.1非pymysql说明真连MySQL、reportlab3.6.12PDF导出用。下面拆解6个关键目录每处都标出你二次开发时必改的文件2.1library/Django主应用所有业务逻辑心脏这是整个系统的根应用settings.py里已配好MySQL连接DATABASES字段指向localhost:3306用户名root密码空开箱即用。重点看三个文件models.py定义BookISBN、分类、库存、Student学号、班级、可借数量、BorrowRecord外键关联书和学生、借阅时间、应还时间、实际归还时间、状态枚举——注意BorrowRecord.status是CharField值为borrowed/returned/overdue不是布尔值为后续罚款计算留扩展空间views.py所有视图函数比如borrow_book(request, book_id)里有完整的事务处理先检查库存0再扣库存再创建记录任一环节失败则transaction.rollback()admin.py已注册全部模型BookAdmin类里加了list_filter [category, status]BorrowRecordAdmin里重写了get_queryset()方法自动过滤掉已删除记录软删除用is_deletedFalse字段。2.2templates/纯前端页面Vue组件竟藏在.bak文件里看到index.html.bak、update-password.vue.bak这些文件别慌——这是作者开发时用Vue写前端但最终交付版切回Django模板index.html才是正式首页。.bak文件是历史残留可安全删除。真正用的是templates/library/下的Django模板base.html定义全局CSS/JS加载Bootstrap 4 CDN已写死book_list.html用{% for book in books %}循环渲染每本书卡片含“借阅”按钮href{% url borrow book.id %}borrow_record.html表格展示借阅记录关键在{% if record.is_overdue %}span classtext-danger逾期/span{% endif %}这个判断逻辑来自BorrowRecord模型的is_overdue属性方法见2.1节models.py不是前端算的。2.3static/静态资源集中营连打印样式都配好了static/css/print.css专门优化浏览器打印效果隐藏导航栏、按钮放大字体表格加边框——答辩现场导出PDF给导师看就靠它。static/js/main.js只有87行但干了三件事1借阅按钮点击后禁用防止重复提交2归还操作弹窗确认3用moment.js已引入格式化日期显示为“2023-05-12”。别小看这三行JS我见过太多毕设因为没防重复提交导致同一本书被借出两次。2.4migrations/数据库迁移史0001_initial.py是你的救命稻草migrations/0001_initial.py里定义了所有表结构包括Book表的stock models.IntegerField(default1)和BorrowRecord表的fine_amount models.DecimalField(max_digits6, decimal_places2, default0.00)。如果你要加字段比如给Student加phone别手写SQL用python manage.py makemigrations library --name add_phone_field生成新迁移文件再migrate——这是Django规范答辩时导师问“怎么保证数据库变更可追溯”你就亮这个文件夹。2.5docs/论文写作指南不是说明书而是答辩话术库docs/需求分析.md里列了真实业务约束“单个学生最多借5本书”、“逾期每天罚0.5元上限50元”、“寒暑假期间不计逾期天数”——这些不是空话全在models.py的BorrowRecord.clean()方法里校验。docs/测试用例.xlsx更狠第3行写着“测试场景学生A借书后未归还系统在应还日后第2天自动将状态改为overdue”对应代码在management/commands/check_overdue.py自定义Django命令每天凌晨执行。写论文“测试章节”时直接抄这个表格导师会觉得你真测了。2.6scripts/批处理全家桶.bat文件藏着部署玄机安装.bat本质是pip install -r requirements.txt运行.bat执行python manage.py runserver 0.0.0.0:8000并自动打开浏览器最关键是3-build.bat它调用python manage.py collectstatic --noinput把所有静态文件复制到staticfiles/目录这是上线Nginx部署必需步骤。很多同学毕设跑不通就是因为跳过了这一步直接拿开发模式部署。提示2-run.bat里有一行python manage.py migrate这是首次运行必执行的。如果报错“Table library_book doesnt exist”说明MySQL服务没启动或数据库名不对——检查settings.py里的NAME字段默认是library_db需提前在MySQL里CREATE DATABASE library_db CHARACTER SET utf8mb4;3. 核心功能实现借阅-归还-罚款三步闭环代码逐行拆解系统价值不在界面有多炫而在业务逻辑是否闭环。我挑最易翻车的“借阅”和“罚款”两个功能把关键代码贴出来告诉你每行为什么这么写、改哪里能适配你的学校规则。3.1 借阅功能事务安全库存强校验不是简单insertlibrary/views.py中borrow_book函数是核心from django.db import transaction from django.shortcuts import get_object_or_404, redirect from django.contrib import messages from .models import Book, Student, BorrowRecord def borrow_book(request, book_id): if request.method ! POST: return redirect(book_list) # 1. 用select_for_update()加行锁防并发超借 book get_object_or_404(Book.objects.select_for_update(), idbook_id) student get_object_or_404(Student, userrequest.user) # 2. 库存检查放事务内避免检查后被别人抢走 with transaction.atomic(): if book.stock 0: messages.error(request, 该书库存不足) return redirect(book_list) # 3. 扣库存创建记录必须原子执行 book.stock - 1 book.save() BorrowRecord.objects.create( bookbook, studentstudent, borrow_datetimezone.now(), due_datetimezone.now() timedelta(days30), # 默认30天 statusborrowed ) messages.success(request, f成功借阅《{book.title}》) return redirect(my_borrows)逻辑说明与参数说明select_for_update()Django ORM的行级锁当多个学生同时点同一本书的“借阅”数据库会排队执行避免stock被减成负数transaction.atomic()包裹扣库存和创建记录确保要么全成功要么全回滚due_date计算timedelta(days30)是默认值若你学校规定教师可借60天、学生30天改这里即可建议抽成配置项见第6章messagesDjango内置消息框架redirect后页面顶部会显示绿色提示比print()调试强十倍。3.2 罚款计算状态机驱动定时任务不是借的时候就算罚款逻辑藏在BorrowRecord模型里这才是高阶设计# library/models.py from django.db import models from django.utils import timezone from decimal import Decimal class BorrowRecord(models.Model): STATUS_CHOICES [ (borrowed, 已借出), (returned, 已归还), (overdue, 已逾期), ] book models.ForeignKey(Book, on_deletemodels.CASCADE) student models.ForeignKey(Student, on_deletemodels.CASCADE) borrow_date models.DateTimeField() due_date models.DateTimeField() return_date models.DateTimeField(nullTrue, blankTrue) status models.CharField(max_length10, choicesSTATUS_CHOICES, defaultborrowed) fine_amount models.DecimalField(max_digits6, decimal_places2, default0.00) def save(self, *args, **kwargs): # 1. 归还时自动计算罚款 if self.status returned and not self.return_date: self.return_date timezone.now() self._calculate_fine() # 2. 逾期状态自动更新借阅时不会触发 if self.status borrowed and self.due_date timezone.now(): self.status overdue self._calculate_fine() super().save(*args, **kwargs) def _calculate_fine(self): 按天计费寒暑假不计此处简化为只算工作日 if not self.return_date: # 未归还按今天计算逾期天数 overdue_days (timezone.now().date() - self.due_date.date()).days else: # 已归还按归还日计算 overdue_days (self.return_date.date() - self.due_date.date()).days # 简化规则每天0.5元上限50元 fine min(Decimal(0.5) * overdue_days, Decimal(50.00)) self.fine_amount max(fine, Decimal(0.00)) # 不为负 property def is_overdue(self): 供模板调用是否逾期 return self.status overdue and self.due_date timezone.now()逻辑说明与参数说明_calculate_fine()方法overdue_days用.date()转为日期对象再相减避免时分秒干扰min()确保罚款不超50元max()防负数property is_overdue模板里{% if record.is_overdue %}调用的就是这个不用每次在view里算好传过去关键点罚款不是借书时算的而是在save()里动态计算——这意味着你手动修改return_date字段罚款会自动重算符合业务真实场景。3.3 定时检查逾期每天凌晨执行不是靠前端轮询library/management/commands/check_overdue.py是Django自定义命令# library/management/commands/check_overdue.py from django.core.management.base import BaseCommand from django.utils import timezone from library.models import BorrowRecord class Command(BaseCommand): help 检查并更新逾期借阅记录 def handle(self, *args, **options): now timezone.now() # 查找所有已借出且应还日期已过期的记录 overdue_records BorrowRecord.objects.filter( statusborrowed, due_date__ltnow ) updated_count 0 for record in overdue_records: record.status overdue record.save() # 触发save()里的_fine计算 updated_count 1 self.stdout.write( self.style.SUCCESS(f成功更新{updated_count}条逾期记录) )怎么让它每天跑在Windows计划任务里添加python manage.py check_overdue触发时间设为每天00:00。Linux下用crontab0 0 * * * cd /path/to/project python manage.py check_overdue。这比前端JS定时器靠谱一万倍——服务器关机它也不丢。4. 避坑指南我踩过的5个血泪坑现在帮你垫在脚下这项目看着简单但毕设答辩前48小时90%的翻车都发生在这几个点。我把A同学和我自己踩的坑整理成清单按“现象→原因→解决”写清楚全是实测有效的后悔药。4.1 现象运行.bat双击后黑窗口一闪而过浏览器打不开http://127.0.0.1:8000原因MySQL服务未启动或settings.py里数据库密码错误默认空密码但你的MySQL可能设了密码。runserver启动失败会立即退出看不到错误日志。解决先手动启动MySQLWinR输入services.msc找到MySQL80服务右键“启动”手动运行命令看报错在项目根目录打开CMD执行python manage.py runserver此时错误会留在窗口里若报Access denied for user rootlocalhost打开library/settings.py找到DATABASES把PASSWORD: 改成你MySQL的实际密码。4.2 现象首页能打开但点“借阅”报NoReverseMatch错误原因URL路由没配对。urls.py里path(borrow/int:book_id/, views.borrow_book, nameborrow)的nameborrow必须和模板里{% url borrow book.id %}的字符串完全一致大小写、下划线都不能错。解决检查library/urls.py确认app_name library已声明否则url标签要写library:borrow在模板里搜索url 确认所有url标签的name值都在urls.py的path(..., namexxx)里定义过终极办法在views.py里临时加print(borrow view called)看是否进得去——如果进不去100%是URL路由问题。4.3 现象借书成功但BorrowRecord表里fine_amount一直是0.00原因BorrowRecord.save()没触发罚款计算。常见于两种情况1你手动在Admin后台创建记录没填due_date导致due_date now为False2save()方法被覆盖但没调用super().save()。解决检查models.py里BorrowRecord.save()末尾是否有super().save(*args, **kwargs)在Admin后台编辑一条记录手动填due_date为昨天的日期再点“保存”看fine_amount是否变0.5如果还不行在save()里加print(fstatus: {self.status}, due_date: {self.due_date})确认条件是否满足。4.4 现象导出PDF报表时中文乱码全是方块原因reportlab默认字体不支持中文docs/说明文档.md里提过要替换字体但源码没做。解决下载simhei.ttf黑体放到static/fonts/目录修改library/views.py里导出PDF的函数通常是export_report在创建SimpleDocTemplate后加from reportlab.pdfbase import pdfmetrics from reportlab.pdfbase.ttfonts import TTFont pdfmetrics.registerFont(TTFont(SimHei, static/fonts/simhei.ttf)) styles getSampleStyleSheet() styles[Normal].fontName SimHei styles[Heading1].fontName SimHei重启服务再导出。4.5 现象答辩演示时导师说“借3本书第4本应该不让借”但系统没拦住原因Student模型里max_borrow_count models.IntegerField(default5)字段存在但borrow_book视图里没校验。原代码只校验了库存没校验学生当前借阅数。解决在borrow_book函数里with transaction.atomic():之前加current_borrows BorrowRecord.objects.filter( studentstudent, status__in[borrowed, overdue] ).count() if current_borrows student.max_borrow_count: messages.error(request, f已达最大借阅数{student.max_borrow_count}本) return redirect(book_list)然后在StudentAdmin里把max_borrow_count设为可编辑字段导师就能在后台改规则了。注意所有修改后务必执行python manage.py makemigrations和migrate否则数据库表结构不更新字段永远读不到。5. 本地运行四步法从解压到打开首页10分钟搞定附各环节验证点别被一堆.bat文件搞晕我给你捋成四步傻瓜流程每步都有明确验证点卡在哪一步就回头查哪一步。5.1 第一步环境准备——确认Python和MySQL就位操作WinR输入cmd执行python --version确认≥3.6执行mysql --version确认MySQL已安装若无去mysql.com下社区版装时勾选“Add to PATH”解压毕设源代码.rar到纯英文路径如D:\library_system严禁中文路径。验证点CMD里能执行python和mysql命令且解压后D:\library_system\manage.py存在。5.2 第二步依赖安装——用安装.bat但要看输出操作双击安装.bat或手动执行pip install -r requirements.txt等待窗口出现Successfully installed...且无红色报错。验证点CMD窗口最后一行是Successfully installed django-3.2.23 mysqlclient-2.1.1等不是ERROR: Could not find a version that satisfies...。若报mysqlclient安装失败按提示装Visual Studio Build Tools官网下载装时勾选“C build tools”。5.3 第三步数据库初始化——2-run.bat是关键操作确保MySQL服务已启动见4.1节双击2-run.bat它会依次执行migrate建表 →createsuperuser建管理员 →runserver启服务当看到Starting development server at http://127.0.0.1:8000/时按CtrlC停止别关窗口。验证点2-run.bat执行中出现Operations to perform:和Running migrations:说明表建成功出现Username (leave blank to use xxx):时输admin回车Email address:直接回车Password:输123456必须≥8位再确认一次最后一行是Django version 3.2.23不是ModuleNotFoundError。5.4 第四步启动服务——运行.bat打开首页操作双击运行.bat窗口显示Starting development server at http://127.0.0.1:8000/后自动打开浏览器地址栏输入http://127.0.0.1:8000/admin/用admin/123456登录能看到Django Admin后台。验证点浏览器打开http://127.0.0.1:8000/显示首页顶部有“图书馆管理系统”Logohttp://127.0.0.1:8000/admin/能登录左侧有Books、Students、Borrow records三个菜单点Books→ADD BOOK填书名、ISBN、库存点SAVE再刷新首页新书出现在列表里——闭环验证完成。提示若运行.bat后浏览器打不开手动在浏览器地址栏输入http://127.0.0.1:8000/若提示“此网站无法提供安全连接”忽略这是开发模式HTTP协议不是HTTPS。6. 二次开发实战3个高频改造需求从改配置到加功能答辩通过后导师常会说“能不能加个XX功能”——别慌这项目设计时就预留了扩展点。我挑三个最常被问到的需求给出可直接抄的代码和验证方法让你改完立刻见效。6.1 需求1把借阅期限从30天改成“教师60天、学生30天”改造思路把硬编码的timedelta(days30)变成根据用户角色动态计算。操作步骤在library/models.py的Student模型里加一个role字段class Student(models.Model): ROLE_CHOICES [ (student, 学生), (teacher, 教师), ] # ...其他字段 role models.CharField(max_length10, choicesROLE_CHOICES, defaultstudent)在library/admin.py里让StudentAdmin显示role字段class StudentAdmin(admin.ModelAdmin): list_display [student_id, name, role, max_borrow_count] list_filter [role] # 后台可按角色筛选改borrow_book视图里的due_date计算# 替换原来的 due_datetimezone.now() timedelta(days30) if student.role teacher: due_days 60 else: due_days 30 due_date timezone.now() timedelta(daysdue_days)验证方法在Admin后台新建一个roleteacher的学生用该学生账号登录借一本书查看BorrowRecord表due_date应是当前时间60天再用普通学生借due_date应是30天。6.2 需求2增加“图书分类统计报表”导出Excel改造思路Django没内置Excel导出但openpyxl库轻量够用比pandas更适合毕设。操作步骤安装库pip install openpyxl在library/views.py里加新视图from openpyxl import Workbook from openpyxl.styles import Font from django.http import HttpResponse def category_report(request): # 按分类统计图书数量和总库存 from django.db.models import Count, Sum categories Book.objects.values(category).annotate( total_booksCount(id), total_stockSum(stock) ).order_by(-total_stock) # 创建Excel wb Workbook() ws wb.active ws.title 图书分类统计 # 表头 headers [分类, 图书种数, 总库存] for col, header in enumerate(headers, 1): cell ws.cell(row1, columncol, valueheader) cell.font Font(boldTrue) # 数据 for row, cat in enumerate(categories, 2): ws.cell(rowrow, column1, valuecat[category]) ws.cell(rowrow, column2, valuecat[total_books]) ws.cell(rowrow, column3, valuecat[total_stock]) # 响应 response HttpResponse(content_typeapplication/vnd.openxmlformats-officedocument.spreadsheetml.sheet) response[Content-Disposition] attachment; filenamecategory_report.xlsx wb.save(response) return response在library/urls.py里加路由path(report/category/, views.category_report, namecategory_report),在templates/library/base.html的导航栏里加链接a classnav-link href{% url category_report %}分类统计/a验证方法点导航栏“分类统计”浏览器自动下载category_report.xlsx打开Excel确认有三列数据且数字与后台Book表统计一致。6.3 需求3接入校园一卡通用学号直接登录去掉Django默认用户改造思路Django认证系统可替换用AUTHENTICATION_BACKENDS指定自定义后端查一卡通数据库假设叫campus_card的student_info表。操作步骤在library/backends.py新建文件里写认证后端from django.contrib.auth.backends import ModelBackend from django.contrib.auth.models import User from .models import Student class CampusCardBackend(ModelBackend): def authenticate(self, request, usernameNone, passwordNone, **kwargs): if username is None or password is None: return None try: # 假设一卡通数据库有student_info表字段card_no, name, class_name # 这里用Django ORM查实际需配置另一个数据库 student Student.objects.get(student_idusername) # 学号即密码校园卡默认规则 if password username: return student.user # 返回关联的Django User except Student.DoesNotExist: pass return None在library/settings.py里替换认证后端AUTHENTICATION_BACKENDS [ library.backends.CampusCardBackend, # 自定义优先 django.contrib.auth.backends.ModelBackend, # 默认备选 ]在library/models.py的Student模型里确保有user models.OneToOneField(User, on_deletemodels.CASCADE)字段原代码已有。验证方法用任意学号如2021001和相同密码登录首页登录成功后request.user.username应等于学号request.user.student.name能取到学生姓名。从那以后我每次帮同学改毕设都强制走一遍“四步法”验证环境→依赖→数据库→服务。哪怕多花10分钟也比答辩当天黑屏强。这套流程跑通了你手里就不是一份“能交差”的代码而是一个随时能演示、能改、能讲清设计逻辑的完整系统。希望帮到你。本文还有配套的精品资源点击获取