简介这是一套面向高校学生与Python初学者、用于课程设计或数据库大作业的库存管理系统完整资料包含源码、SQL文件与设计报告三部分可帮助读者理解从需求分析到功能落地的完整开发流程。压缩包共1473个文件约28.52MB以ts、html、js、scss、less等前端资源为主辅以py、pyc等Python源码与编译文件另有sql、sqlite3数据库文件及md、pdf、docx等说明文档目录结构清晰便于按模块查阅。系统覆盖商品信息管理、库存信息管理、入库出库操作、库存查询统计与用户权限管理等核心模块设计报告则对数据库表结构、字段约束与表间关系作了详细阐述。目前已有44人学习下载适合需要参考完整项目结构、数据库设计思路与Python业务实现细节的读者借鉴使用。1. 从一份课程设计说起Python 库存管理系统到底要交付什么每年到了期末总有一批人卡在“库存管理系统”这个选题上。标题里写着“源码sql文件设计报告”看起来三样东西齐了就能交差但真正动手才发现源码跑不起来、sql 文件导入报错、设计报告和代码对不上号。我带过几届学生的课程设计也帮朋友改过类似的单子最常见的翻车现场不是代码写得多烂而是环境没配对、数据库没连上、表结构跟代码里的字段名差一个字母。这个方案适合谁一是正在做课程设计、需要一套能跑通的 Python 库存管理系统的人二是想拿它当练手项目、把增删改查和数据库操作串一遍的入门者。它解决的核心问题很具体用 Python 把商品入库、出库、库存查询、低库存预警这几个动作跑通数据落到 MySQL 里最后能生成一份说得清设计思路的报告。下面我按实际落地的顺序把环境、表结构、核心代码、避坑点拆开讲。2. 环境与依赖把 Python 和 MySQL 先接上2.1 为什么选 PyMySQL 而不是 MySQLdbPython 连 MySQL 的库有好几个常见的是 MySQLdb、PyMySQL、mysql-connector-python。MySQLdb 在 Windows 上装起来经常卡在编译环节mysql-connector 是官方出品但 API 风格跟课程设计里常见的写法不太一样。PyMySQL 是纯 Python 实现pip 一条命令就能装代码里import pymysql直接能用对新手最友好。我一般会先确认 Python 版本3.8 到 3.11 都行再装依赖。# 确认 Python 版本建议 3.8 以上 python --version # 安装 PyMySQL这是连 MySQL 的核心库 pip install pymysql # 如果要做界面装 tkinter 相关标准库自带一般不用额外装 # 如果要用到日期处理装 datetime标准库自带装完之后别急着写代码先确认 MySQL 服务是活的。Windows 下在服务里看 MySQL 是否运行Linux 下用systemctl status mysql。这一步跳过的人后面报Cant connect to MySQL server的时候会找半天。2.2 建库建表的 sql 文件怎么导入标题里的“sql 文件”通常包含建库、建表、插初始数据三部分。我习惯把库名定为inventory_db字符集用utf8mb4不然商品名里的中文会变问号。导入方式有两种命令行和图形工具。命令行更稳不容易因为工具版本差异出问题。# 登录 MySQL输入密码 mysql -u root -p # 在 MySQL 命令行里执行 sql 文件 source /path/to/inventory.sql; # 或者退出后用命令行直接导入 mysql -u root -p inventory_db /path/to/inventory.sql导入之后一定要验证use inventory_db; show tables;看表在不在select * from product limit 5;看数据有没有进去。我见过有人导入时没指定库结果表建到了别的库里代码连上去查不到表报Table inventory_db.product doesnt exist。2.3 数据库连接配置的写法与参数说明连接配置我一般单独放一个db_config.py不硬编码在主程序里。这样换环境只改一个文件。关键参数有五个host、port、user、password、database再加一个 charset。# db_config.py import pymysql def get_connection(): return pymysql.connect( hostlocalhost, # 数据库地址本机就是 localhost port3306, # MySQL 默认端口改过就填改后的 userroot, # 用户名 passwordyour_pass, # 换成你自己的密码 databaseinventory_db, # 库名要和 sql 文件里一致 charsetutf8mb4, # 字符集防止中文乱码 cursorclasspymysql.cursors.DictCursor # 让查询结果以字典返回 )cursorclass设成DictCursor之后fetchone()返回的是{product_name: 螺丝刀, stock: 20}这种形式取值用row[product_name]比用下标row[1]可读性好得多。密码不要写死成123456然后提交到公开仓库这是血泪经验改起来一分钟泄露了很麻烦。3. 核心功能落地入库、出库、查询、预警怎么写3.1 商品表的字段设计与 sql 建表语句表结构设计不好后面代码怎么写都别扭。商品表我一般叫product字段包括自增主键id、商品编号product_code唯一、商品名product_name、规格spec、库存数量stock、库存下限min_stock、创建时间create_time。库存下限是用来做预警的低于这个值就提示补货。CREATE TABLE product ( id INT NOT NULL AUTO_INCREMENT, product_code VARCHAR(50) NOT NULL UNIQUE COMMENT 商品编号, product_name VARCHAR(100) NOT NULL COMMENT 商品名称, spec VARCHAR(100) DEFAULT NULL COMMENT 规格, stock INT NOT NULL DEFAULT 0 COMMENT 当前库存, min_stock INT NOT NULL DEFAULT 10 COMMENT 库存下限, create_time DATETIME DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (id) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4;product_code加 UNIQUE 约束防止重复录入同一个商品。stock和min_stock用 INT别用 VARCHAR不然排序和比较会出玄学问题。create_time默认当前时间插入时不用手动传。3.2 入库操作的完整函数与事务处理入库不是简单地把 stock 加一下。正确做法是先查商品是否存在存在就更新库存不存在就插入新商品整个过程放在一个事务里。这样中途出错可以回滚不会出现“库存加了但日志没记”的情况。import pymysql from db_config import get_connection def stock_in(product_code, product_name, spec, quantity, min_stock10): 入库操作商品存在则增加库存不存在则新建 quantity: 入库数量必须为正整数 if quantity 0: return False, 入库数量必须大于 0 conn get_connection() try: with conn.cursor() as cursor: # 先查商品是否存在 sql_check SELECT id, stock FROM product WHERE product_code %s cursor.execute(sql_check, (product_code,)) result cursor.fetchone() if result: # 存在更新库存 new_stock result[stock] quantity sql_update UPDATE product SET stock %s WHERE product_code %s cursor.execute(sql_update, (new_stock, product_code)) else: # 不存在插入新记录 sql_insert INSERT INTO product (product_code, product_name, spec, stock, min_stock) VALUES (%s, %s, %s, %s, %s) cursor.execute(sql_insert, (product_code, product_name, spec, quantity, min_stock)) # 记录入库日志假设有 log 表 sql_log INSERT INTO stock_log (product_code, change_type, change_qty) VALUES (%s, IN, %s) cursor.execute(sql_log, (product_code, quantity)) conn.commit() return True, 入库成功 except Exception as e: conn.rollback() return False, f入库失败{str(e)} finally: conn.close()with conn.cursor()这种写法会自动关闭游标不用手动cursor.close()。conn.commit()放在with外面确保所有操作都成功才提交。except里先rollback()再返回错误信息这样数据不会脏。参数用%s占位不要用字符串拼接否则商品名里带单引号就会 SQL 报错这也是常见的注入隐患。3.3 出库与库存不足的判断逻辑出库比入库多一层判断库存够不够。不够就直接拒绝不要先减再补。判断和更新要在同一个事务里避免并发时超卖。def stock_out(product_code, quantity): 出库操作先判断库存是否充足充足则扣减 if quantity 0: return False, 出库数量必须大于 0 conn get_connection() try: with conn.cursor() as cursor: # 加 FOR UPDATE 锁住这一行防止并发扣减 sql_check SELECT stock FROM product WHERE product_code %s FOR UPDATE cursor.execute(sql_check, (product_code,)) result cursor.fetchone() if not result: return False, 商品不存在 if result[stock] quantity: return False, f库存不足当前库存 {result[stock]} sql_update UPDATE product SET stock stock - %s WHERE product_code %s cursor.execute(sql_update, (quantity, product_code)) sql_log INSERT INTO stock_log (product_code, change_type, change_qty) VALUES (%s, OUT, %s) cursor.execute(sql_log, (product_code, quantity)) conn.commit() return True, 出库成功 except Exception as e: conn.rollback() return False, f出库失败{str(e)} finally: conn.close()FOR UPDATE是行级锁在事务里锁住查询到的行别的事务要改这一行就得等。课程设计里并发场景不多但写上这个能体现你考虑过数据一致性。注意FOR UPDATE必须在事务里用autocommit 模式下不生效。3.4 库存查询与低库存预警的 sql 写法查询分两种按商品编号精确查和查全部。预警就是查stock min_stock的记录。sql 本身不复杂关键是返回结果的处理。def query_stock(product_codeNone): 查询库存传编号查单个不传查全部 conn get_connection() try: with conn.cursor() as cursor: if product_code: sql SELECT * FROM product WHERE product_code %s cursor.execute(sql, (product_code,)) return cursor.fetchone() else: sql SELECT * FROM product ORDER BY stock ASC cursor.execute(sql) return cursor.fetchall() finally: conn.close() def low_stock_alert(): 返回所有低于库存下限的商品 conn get_connection() try: with conn.cursor() as cursor: sql SELECT * FROM product WHERE stock min_stock cursor.execute(sql) return cursor.fetchall() finally: conn.close()ORDER BY stock ASC让库存少的排前面一眼能看到哪些快没了。预警查询用而不是因为等于下限时通常还算安全低于才需要补。这个细节设计报告里可以写一句显得有思考。4. 避坑与排查那些让系统跑不起来的常见问题4.1 中文乱码从建库到连接的三处字符集现象商品名存进去变成???或者乱码。原因通常有三处建库时没指定字符集、建表时没指定、连接时没指定。解决方法是三处都统一成utf8mb4。建库语句写成CREATE DATABASE inventory_db DEFAULT CHARSET utf8mb4;建表末尾加DEFAULT CHARSETutf8mb4连接参数里加charsetutf8mb4。三处缺一处都可能出问题我一般建完库先show variables like character%;确认一下。4.2 导入 sql 文件报 1064 或 1046现象source导入时提示语法错误或者“No database selected”。原因一是 sql 文件里没有USE inventory_db;二是文件编码不是 UTF-8。解决方法是打开 sql 文件在开头手动加一行USE inventory_db;并确认文件保存为 UTF-8 无 BOM 格式。Windows 记事本默认可能带 BOM用 VS Code 或 Notepad 转一下。4.3 连接报 2003MySQL 服务没起或端口不对现象pymysql.err.OperationalError: (2003, Cant connect to MySQL server on localhost)。原因一是 MySQL 服务没启动二是端口不是 3306三是防火墙拦了。解决方法是先net start mysqlWindows或systemctl start mysqlLinux再确认端口show variables like port;能看到实际端口。如果改过端口连接配置里也要改。4.4 出库后库存变负数现象明明判断了库存不足还是出现了负数。原因通常是判断和更新之间没有锁两个请求同时读到相同库存都判断通过然后都扣减。解决方法是像 3.3 那样用FOR UPDATE锁行或者把判断和更新合并成一条 sqlUPDATE product SET stock stock - %s WHERE product_code %s AND stock %s看影响行数是否为 1。后者更简洁推荐。4.5 设计报告和代码对不上现象报告里写的表字段和代码里用的不一致或者报告里的功能代码里没实现。原因是先写报告后写代码或者改代码没同步改报告。解决方法是先定表结构代码按表结构写报告里的 ER 图和字段说明直接从DESC product;的输出复制。改代码时顺手在报告里标一下别等最后对不上再返工。5. 进阶技巧把系统从“能跑”推到“好用”5.1 用上下文管理器简化数据库操作每次写try...finally关连接太啰嗦。可以写一个上下文管理器把连接和关闭包起来。from contextlib import contextmanager contextmanager def db_cursor(): conn get_connection() try: with conn.cursor() as cursor: yield cursor conn.commit() except Exception: conn.rollback() raise finally: conn.close() # 用的时候 with db_cursor() as cursor: cursor.execute(SELECT * FROM product) rows cursor.fetchall()这样业务代码里不用重复写 commit 和 close出错自动回滚。yield之前是进入时的操作之后是退出时的操作。注意conn.commit()放在with conn.cursor()外面确保游标关闭后再提交。5.2 给查询加分页避免数据多了卡死商品上千条之后fetchall()一次全取出来会占内存界面也会卡。加 LIMIT 和 OFFSET 分页。def query_stock_page(page1, page_size20): offset (page - 1) * page_size conn get_connection() try: with conn.cursor() as cursor: sql SELECT * FROM product ORDER BY id LIMIT %s OFFSET %s cursor.execute(sql, (page_size, offset)) return cursor.fetchall() finally: conn.close()page从 1 开始offset是跳过的行数。page_size一般 20 到 50看界面能显示多少行。别忘了再写一个count查询算总页数不然不知道有多少页。5.3 用日志表追溯每一次库存变动只改product表的 stock出了问题查不到是谁改的、什么时候改的。加一张stock_log表每次入库出库都插一条。CREATE TABLE stock_log ( id INT NOT NULL AUTO_INCREMENT, product_code VARCHAR(50) NOT NULL, change_type ENUM(IN,OUT) NOT NULL, change_qty INT NOT NULL, change_time DATETIME DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (id) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4;change_type用 ENUM 限制只能填 IN 或 OUT防止写错。查某个商品的所有变动SELECT * FROM stock_log WHERE product_code P001 ORDER BY change_time DESC;。这个表在写设计报告时也是加分项说明你考虑了审计和追溯。5.4 验证系统是否真的跑通三个必做测试写完代码别急着交跑三个测试。第一入库一个新商品查库存是否为入库数量。第二出库数量大于库存看是否返回“库存不足”且库存没变。第三把某个商品库存改到低于下限调预警函数看是否返回该商品。三个都过了基本功能就没问题。我习惯把测试结果截图放进设计报告比空口说“已实现”有说服力。最后说个习惯每次改完代码先把db_config.py里的密码换成自己的再跑别用别人的配置。数据库连接失败时先看服务起没起再看端口和密码最后看库名和表名。这个顺序能省很多时间。希望帮到你。本文还有配套的精品资源点击获取
阅读完成 · 觉得有帮助?