简介这份资源是一套面向高校课程设计与数据库实践学习者的图书管理系统完整项目采用Python结合SQL Server实现Web端应用业务模式参考学校图书馆借阅流程。系统分为借阅者与管理者两大角色学生和老师可登录、借阅、还书、延期及退出管理者涵盖用户管理、图书上架修改下架以及超级管理查看操作记录适合用于数据库课程设计、毕业设计参考或Web全栈入门练手。压缩包共183个文件约11.63MB包含16个py源码、27个pyd扩展、23个html页面、13个js脚本、10个css样式以及png截图、xmind思维导图、docx文档和依赖wheel等覆盖前端页面、后端逻辑与运行环境配置。目前已有701人学习下载可帮助读者快速理解图书借阅业务建模、角色权限划分与SQL Server数据表设计思路并对照完整目录结构梳理项目模块为二次开发或答辩准备提供参考。1. 从一份课程设计说起Python SQL Server 的图书管理系统能跑出什么如果你正在做数据库课程设计或者需要一套能讲清楚「前后端 数据库」完整链路的 Web 项目这套基于 Python 和 SQL Server 的图书管理系统值得拆开看看。它的业务模型直接对标学校图书馆的借阅流程不是那种只增删改查一张表的玩具项目。系统里区分了学生/老师、图书馆员工、管理者、超级管理四类角色每类角色的操作边界都不一样借阅者能登录、借书、还书、续借、退出员工能上架、修改、下架图书管理者能处理用户借阅问题超级管理还能查看其他管理者的操作记录。这种多角色权限设计恰好是课程设计里最容易讲出深度的地方。整套代码用 Python 做后端逻辑SQL Server 做数据持久化前端走 Web 页面结构上适合拿来改造成自己的作业也适合作为理解「业务系统怎么落地」的参照。2. 环境搭建与项目结构从 activate.bat 到 bootstrap 的完整链路2.1 为什么选 Python SQL Server 这套组合课程设计选技术栈第一原则是「能跑通、能讲清、能改」。Python 做 Web 后端常见做法是用 Flask 或 Django前者轻量、代码量少适合把注意力放在数据库交互上后者自带 ORM 和 Admin但配置项多调试成本高。这套项目从文件结构看走的是轻量路线没有过度封装。SQL Server 作为关系型数据库在课程设计里有个天然优势它的企业管理器SSMS可视化程度高建表、建视图、写存储过程都能图形化操作答辩时演示数据库设计特别方便。相比 MySQLSQL Server 的权限管理更细正好对应系统里「管理者 / 超级管理」的权限分层。如果你学校机房装的是 SQL Server 2012 或 2019这套代码基本不用大改就能连上。2.2 项目目录里那些文件到底管什么拿到压缩包后先别急着运行。把目录结构看清楚后面排错能省一半时间。从项目正文给出的文件清单来看核心文件分三类文件/目录作用是否必须保留activate / activate.bat / deactivate.batPython 虚拟环境激活与退出脚本是虚拟环境隔离依赖pyvenv.cfg虚拟环境配置文件记录 Python 路径和版本是删了虚拟环境会失效bootstrap.css / bootstrap.min.css前端样式框架控制页面布局和组件外观是删了页面会裸奔bootstrap-grid.css / bootstrap-grid.min.cssBootstrap 栅格系统负责响应式排版是影响表格和表单对齐虚拟环境这块有个血泪经验很多同学直接把项目拷到另一台电脑发现 activate.bat 双击闪退。原因通常是 pyvenv.cfg 里写的 Python 安装路径还是原来那台机器的新机器上路径对不上。解决办法是删掉整个虚拟环境文件夹用python -m venv venv重新建一个再装依赖。Bootstrap 的 css 和 min.css 同时存在是为了开发时用未压缩版方便调试部署时换 min 版减小体积。课程设计里保留两套没问题但要知道它们不能同时引入同一个页面否则样式会冲突。2.3 从零把环境跑起来的操作步骤先确认本机 Python 版本。打开命令行执行python --version如果显示 3.8 到 3.11 之间都行这套项目没有用到太新的语法特性。接着进入项目根目录创建虚拟环境cd 图书管理系统目录 python -m venv venvWindows 下激活虚拟环境venv\Scripts\activate.bat激活成功后命令行前面会出现(venv)标识。然后安装依赖。项目没有给出 requirements.txt 的话常见做法是手动装这几个pip install flask pyodbcflask负责 Web 路由和模板渲染pyodbc是 Python 连接 SQL Server 的驱动。注意 pyodbc 在 Windows 上需要系统已安装 ODBC Driver for SQL Server一般装 SQL Server 时会自带如果没有去微软官网下个msodbcsql17或msodbcsql18装上即可。装完后用一段最小代码测试数据库连通性import pyodbc conn pyodbc.connect( DRIVER{ODBC Driver 17 for SQL Server}; SERVERlocalhost; DATABASELibraryDB; UIDsa; PWD你的密码 ) cursor conn.cursor() cursor.execute(SELECT VERSION) print(cursor.fetchone()) conn.close()这段代码里DRIVER要和你本机实际安装的 ODBC 驱动版本一致写 17 报错就换 18。SERVER如果是本机默认实例写localhost或127.0.0.1都行如果是命名实例要写成localhost\SQLEXPRESS这种格式。UID和PWD是 SQL Server 的登录账号课程设计里常用sa但要注意 sa 账号默认可能是禁用的需要在 SSMS 里手动启用并设置密码。跑通这一步后面业务代码才有意义。3. 数据库设计与核心表结构借阅流程怎么落到 SQL Server 里3.1 从业务角色反推表设计图书管理系统的数据库设计核心是把「谁借了什么书、什么时候还、谁操作的」这几件事拆成表。按摘要描述里的角色划分至少需要这几张表Users表存学生、老师、员工、管理者的账号信息用Role字段区分身份Books表存图书基本信息包括书名、作者、ISBN、库存状态BorrowRecords表存借阅记录关联用户和图书记录借出时间、应还时间、实还时间OperationLogs表存管理者的操作记录超级管理查的就是这张表Users表里Role字段的设计有个容易翻车的地方如果直接用字符串存「学生」「老师」「管理员」后面写查询条件时容易拼错。常见做法是用整数枚举比如 1 代表学生、2 代表老师、3 代表员工、4 代表管理者、5 代表超级管理。这样权限判断就是简单的数值比较不容易出错。3.2 建表 SQL 与关键约束下面这段 SQL 可以直接在 SSMS 里执行建出核心表结构CREATE DATABASE LibraryDB; GO USE LibraryDB; GO CREATE TABLE Users ( UserID INT IDENTITY(1,1) PRIMARY KEY, Username NVARCHAR(50) NOT NULL UNIQUE, Password NVARCHAR(100) NOT NULL, Role INT NOT NULL DEFAULT 1, MaxBorrow INT NOT NULL DEFAULT 5, CreatedAt DATETIME DEFAULT GETDATE() ); CREATE TABLE Books ( BookID INT IDENTITY(1,1) PRIMARY KEY, Title NVARCHAR(200) NOT NULL, Author NVARCHAR(100), ISBN NVARCHAR(20) UNIQUE, Stock INT NOT NULL DEFAULT 0, Status INT NOT NULL DEFAULT 1 ); CREATE TABLE BorrowRecords ( RecordID INT IDENTITY(1,1) PRIMARY KEY, UserID INT FOREIGN KEY REFERENCES Users(UserID), BookID INT FOREIGN KEY REFERENCES Books(BookID), BorrowDate DATETIME DEFAULT GETDATE(), DueDate DATETIME NOT NULL, ReturnDate DATETIME NULL, RenewCount INT DEFAULT 0 ); CREATE TABLE OperationLogs ( LogID INT IDENTITY(1,1) PRIMARY KEY, OperatorID INT FOREIGN KEY REFERENCES Users(UserID), Action NVARCHAR(200), TargetID INT, LogTime DATETIME DEFAULT GETDATE() );IDENTITY(1,1)让主键自增省去手动维护 ID 的麻烦。UNIQUE约束保证用户名和 ISBN 不重复这是业务上的硬要求。FOREIGN KEY把借阅记录和用户、图书关联起来删用户或删书时如果有关联记录数据库会直接拒绝避免出现「借阅记录找不到人」的脏数据。MaxBorrow字段控制每个用户最多能借几本学生和老师的上限可以不同这个值在用户管理界面里由管理者调整。3.3 借书与还书的存储过程实现把借书逻辑写成存储过程好处是业务规则集中在数据库层Python 端只负责调用和展示。下面这个存储过程处理借书CREATE PROCEDURE BorrowBook UserID INT, BookID INT, Days INT 30 AS BEGIN DECLARE Stock INT, Borrowed INT, MaxBorrow INT; SELECT Stock Stock FROM Books WHERE BookID BookID; SELECT MaxBorrow MaxBorrow FROM Users WHERE UserID UserID; SELECT Borrowed COUNT(*) FROM BorrowRecords WHERE UserID UserID AND ReturnDate IS NULL; IF Stock 0 BEGIN RAISERROR(图书库存不足, 16, 1); RETURN; END IF Borrowed MaxBorrow BEGIN RAISERROR(已达最大借阅数量, 16, 1); RETURN; END INSERT INTO BorrowRecords (UserID, BookID, DueDate) VALUES (UserID, BookID, DATEADD(DAY, Days, GETDATE())); UPDATE Books SET Stock Stock - 1 WHERE BookID BookID; INSERT INTO OperationLogs (OperatorID, Action, TargetID) VALUES (UserID, 借书, BookID); END;这段逻辑先查库存和已借数量两个条件任一不满足就抛错回滚。RAISERROR抛出的错误信息会被 Python 端捕获展示给用户。DATEADD(DAY, Days, GETDATE())算出应还日期默认 30 天老师可以传更长的天数。最后写一条操作日志超级管理查记录时就有数据来源。还书逻辑类似把ReturnDate更新为当前时间Stock加一同时检查是否逾期。逾期判断用CASE WHEN GETDATE() DueDate THEN 1 ELSE 0 END在查询借阅记录时直接算出来。4. Web 层实现与角色权限控制Flask 路由怎么对应四类用户4.1 登录态与角色判断的中间件写法Web 层最核心的不是页面多好看而是「不同角色登录后看到的东西不一样」。Flask 里常见做法是用 session 存用户 ID 和角色然后写一个装饰器做权限校验from functools import wraps from flask import session, redirect, url_for, abort def login_required(roleNone): def decorator(f): wraps(f) def wrapper(*args, **kwargs): if user_id not in session: return redirect(url_for(login)) if role is not None and session.get(role) not in role: abort(403) return f(*args, **kwargs) return wrapper return decorator用的时候挂在路由上app.route(/admin/books) login_required(role[3, 4, 5]) def manage_books(): # 只有员工、管理者、超级管理能进 passrole参数传一个列表表示允许访问的角色集合。学生和老师访问/admin/books会直接收到 403不会看到管理界面。这个装饰器要放在所有业务路由前面漏掉一个路由就可能出现越权。血泪经验是课程设计答辩时老师最喜欢点「学生账号能不能进管理页面」所以每个管理路由都要检查一遍。4.2 借阅者界面的核心交互学生和老师登录后主要操作是查书、借书、还书、续借。查书页面用LIKE做模糊搜索app.route(/search) login_required(role[1, 2]) def search_books(): keyword request.args.get(q, ) cursor.execute( SELECT BookID, Title, Author, Stock FROM Books WHERE Title LIKE ? OR Author LIKE ?, (% keyword %, % keyword %) ) books cursor.fetchall() return render_template(search.html, booksbooks)?是参数占位符pyodbc 会自动转义避免 SQL 注入。借书操作调用前面写的BorrowBook存储过程app.route(/borrow/int:book_id, methods[POST]) login_required(role[1, 2]) def borrow(book_id): try: cursor.execute(EXEC BorrowBook UserID?, BookID?, (session[user_id], book_id)) cursor.commit() flash(借阅成功) except Exception as e: flash(str(e)) return redirect(url_for(search_books))cursor.commit()必须调用否则存储过程里的 INSERT 和 UPDATE 不会真正写入数据库。异常捕获后把错误信息 flash 到页面用户能看到「库存不足」或「已达最大借阅数量」的具体原因而不是一个白屏。4.3 管理端操作记录与超级管理视图员工和管理者的操作都要写进OperationLogs表。上架图书的代码app.route(/admin/book/add, methods[POST]) login_required(role[3, 4, 5]) def add_book(): title request.form[title] author request.form[author] isbn request.form[isbn] stock int(request.form[stock]) cursor.execute( INSERT INTO Books (Title, Author, ISBN, Stock) VALUES (?, ?, ?, ?), (title, author, isbn, stock) ) cursor.execute( INSERT INTO OperationLogs (OperatorID, Action, TargetID) VALUES (?, ?, SCOPE_IDENTITY()), (session[user_id], 上架图书) ) cursor.commit() return redirect(url_for(manage_books))SCOPE_IDENTITY()拿到刚插入的图书 ID写进日志的TargetID。超级管理查看日志的页面直接查OperationLogs关联Users拿操作人名字SELECT L.LogID, U.Username, L.Action, L.LogTime FROM OperationLogs L JOIN Users U ON L.OperatorID U.UserID ORDER BY L.LogTime DESC;这个查询在数据量大时会慢常见优化是给LogTime加索引。课程设计数据量小不加以免增加复杂度但要知道这个点。5. 避坑与常见问题排查那些让课程设计卡半天的细节5.1 连接 SQL Server 报「登录失败」现象Python 代码运行后抛pyodbc.InterfaceError或Login failed for user sa。原因通常是 sa 账号未启用或者 SQL Server 配置为仅 Windows 身份验证。解决打开 SSMS用 Windows 身份登录在「安全性」→「登录名」里找到 sa右键属性启用登录并重设密码。然后在服务器属性→安全性里把身份验证模式改为「SQL Server 和 Windows 身份验证模式」重启 SQL Server 服务。5.2 中文书名显示成问号现象插入中文图书名后数据库里显示??或乱码。原因建表时用了VARCHAR而不是NVARCHAR或者连接字符串没指定编码。解决所有存中文的字段一律用NVARCHARPython 连接时确保字符串是 unicode。如果已经建了VARCHAR表用ALTER TABLE Books ALTER COLUMN Title NVARCHAR(200)改过来。5.3 虚拟环境激活后 pip 装包报权限错误现象pip install flask提示PermissionError或写入被拒绝。原因虚拟环境建在了需要管理员权限的目录比如C:\Program Files下面。解决把项目移到用户目录比如D:\projects\library重新建虚拟环境。Windows 上还要注意不要用管理员身份开命令行普通权限即可。5.4 借书后库存没减现象借阅记录插入了但Books表的Stock没变。原因存储过程里UPDATE执行了但没COMMIT或者 Python 端调用了存储过程但没cursor.commit()。解决检查存储过程内部是否有未提交的事务Python 端每次写操作后都要显式 commit。另外确认存储过程里UPDATE Books的条件WHERE BookID BookID确实匹配到了行。5.5 页面样式全乱现象HTML 能打开但按钮、表格没有 Bootstrap 样式。原因bootstrap.css路径写错或者同时引入了bootstrap.css和bootstrap.min.css导致冲突。解决在模板里用url_for(static, filenamecss/bootstrap.min.css)生成正确路径只引入一个版本。Flask 的静态文件默认放在static目录下确认文件确实在那个位置。6. 进阶技巧用视图和触发器把业务规则再收紧一层课程设计做到能跑只是及格线。想让答辩老师多问几个「然后呢」可以在数据库层再加两个东西视图和触发器。先建一个借阅详情视图把三张表关联的结果固化下来CREATE VIEW BorrowDetail AS SELECT R.RecordID, U.Username, B.Title, R.BorrowDate, R.DueDate, R.ReturnDate, CASE WHEN R.ReturnDate IS NOT NULL THEN 已还 WHEN GETDATE() R.DueDate THEN 逾期 ELSE 借阅中 END AS Status FROM BorrowRecords R JOIN Users U ON R.UserID U.UserID JOIN Books B ON R.BookID B.BookID;这个视图的好处是Python 端查借阅记录时不用再写三表 JOIN直接SELECT * FROM BorrowDetail WHERE Username ?就行。CASE表达式把状态算好前端直接显示「借阅中」「逾期」「已还」不用在 Python 里写一堆 if-else。状态判断逻辑集中在数据库层改规则时只改视图不用动 Python 代码。再给BorrowRecords加一个触发器防止同一本书被同一人重复借阅CREATE TRIGGER trg_PreventDuplicateBorrow ON BorrowRecords INSTEAD OF INSERT AS BEGIN IF EXISTS ( SELECT 1 FROM inserted I JOIN BorrowRecords R ON I.UserID R.UserID AND I.BookID R.BookID AND R.ReturnDate IS NULL ) BEGIN RAISERROR(该用户已借阅此书且未归还, 16, 1); RETURN; END INSERT INTO BorrowRecords (UserID, BookID, BorrowDate, DueDate, RenewCount) SELECT UserID, BookID, BorrowDate, DueDate, RenewCount FROM inserted; END;INSTEAD OF INSERT触发器在插入前先检查如果发现同一用户对同一本书还有未归还的记录直接拒绝。这样即使 Python 端漏了检查数据库层也能兜住。注意触发器和存储过程里的检查有重叠但两者不冲突存储过程管库存和借阅上限触发器管重复借阅各司其职。验证这两个东西是否生效可以手动跑几条 SQL-- 测试视图 SELECT * FROM BorrowDetail WHERE Status 逾期; -- 测试触发器先借一本再借同一本 EXEC BorrowBook UserID1, BookID1; EXEC BorrowBook UserID1, BookID1; -- 应该报错如果第二条EXEC抛出「该用户已借阅此书且未归还」说明触发器工作正常。视图查询能返回逾期记录说明状态计算正确。最后说个习惯每次改完数据库对象我都会把建表、建视图、建触发器的 SQL 单独存一个schema.sql文件和 Python 代码放一起。这样换台电脑或者重装数据库时直接跑一遍脚本就能恢复环境不用凭记忆手动建。课程设计答辩前把这个文件打开给老师看比口头说「我设计了这些表」有说服力得多。希望帮到你。本文还有配套的精品资源点击获取