2026/10/4 3:17:43

基于Python的药店药品管理系统源码解析与毕业设计实战指南

基于Python的药店药品管理系统源码解析与毕业设计实战指南 简介这是一套面向计算机相关专业学生与Python初学者的药店药品管理系统完整项目源码可直接用于毕业设计、课程设计或自学练手。系统围绕药品信息管理、用户权限、销售记录、库存预警与采购计划等核心业务展开帮助读者理解如何将数据库设计、前后端交互与界面开发整合为可运行的实际应用。压缩包共392个文件约20.63MB包含36个py源码文件、16个vue组件、35个ts脚本及大量jpeg、svg、png等界面素材另有json配置、md说明与less样式文件覆盖后端逻辑、前端页面与静态资源。项目采用前后端分离结构源码分层清晰便于按模块阅读与二次修改。目前已有120人学习下载适合需要完整项目案例、想快速搭建药店管理原型或补充工程实践经验的读者参考借鉴。1. 从一份药店药品管理系统源码说起它到底能解决什么问题很多计算机专业的同学在选题阶段都会卡在同一个地方想做一个能写进简历、答辩时又讲得清楚的项目但不知道从哪下手。药店药品管理系统这个题目之所以每年都有人选是因为它天然具备一个完整的业务闭环——药品入库、库存查询、销售出库、效期预警、用户权限每一个环节都能对应到数据库设计和后端接口设计上答辩老师随便挑一个点都能问出深度。而用 Python 来做意味着你不需要在环境配置上耗掉大半时间可以把精力放在业务逻辑和数据库设计上这对毕业设计或课程设计的周期来说非常关键。这篇文章面向的是拿到了一份「基于 Python 的药店药品管理系统」源码或选题、但不确定怎么把它跑起来、怎么改、怎么讲清楚的人。我会按实际开发顺序从环境搭建、数据库设计、核心模块实现、常见报错排查一直讲到答辩时怎么把这个项目说出技术含量。中间会给出可以直接复现的代码和命令也会标注哪些地方容易翻车。如果你正在做基于 Python 的毕业设计或数据库课程设计这篇内容可以当作一份落地参考。2. 环境搭建与项目结构把源码跑起来的最小路径2.1 Python 版本选择与依赖安装拿到一份 Python 项目源码第一件事不是急着看代码而是先把运行环境对齐。药店药品管理系统这类项目通常依赖 Flask 或 Django 做 Web 层MySQL 或 SQLite 做数据存储前端可能是简单的 HTML 模板或者 Vue 构建的页面。不同项目对 Python 版本的要求不一样但绝大多数教学类项目在 Python 3.8 到 3.11 之间都能跑通。如果你本地装的是 Python 3.12 以上部分老依赖可能会编译失败这时候用 conda 或 pyenv 切一个 3.9 的环境是最省事的做法。安装依赖之前先看项目根目录有没有 requirements.txt。有的话直接装没有的话根据 import 语句反推。下面是一个典型的安装流程# 创建虚拟环境避免污染全局包 python -m venv venv # 激活虚拟环境Windows venv\Scripts\activate # 激活虚拟环境macOS/Linux source venv/bin/activate # 安装依赖建议加国内镜像加速 pip install -r requirements.txt -i https://pypi.tuna.tsinghua.edu.cn/simple # 如果项目没有 requirements.txt手动装核心依赖 pip install flask flask-sqlalchemy pymysql flask-login这里有几个参数值得说明。-i后面跟的是镜像地址能明显加快下载速度尤其是 numpy、pandas 这类包。虚拟环境的作用是隔离依赖避免你本机其他项目的包版本冲突。如果安装过程中出现error: Microsoft Visual C 14.0 is required说明某个包需要编译最直接的办法是去下载对应的 whl 文件手动安装或者换一个不需要编译的版本。提示不要用pip install不带版本号直接装最新版教学项目里经常出现某个包升级后 API 变了导致启动报错的情况。requirements.txt 里锁定的版本尽量别动。2.2 项目目录结构与入口文件定位一个结构清晰的药店药品管理系统通常长这样pharmacy-system/ ├── app.py # 入口文件启动 Flask 应用 ├── config.py # 数据库连接、密钥等配置 ├── models.py # 数据模型定义 ├── routes/ # 路由和视图函数 │ ├── drug.py # 药品相关接口 │ ├── stock.py # 库存相关接口 │ └── user.py # 用户和权限 ├── templates/ # HTML 模板 ├── static/ # CSS、JS、图片 ├── requirements.txt └── README.md拿到源码后先找入口文件一般是app.py、main.py或run.py。打开它看if __name__ __main__:下面的启动参数确认端口号和 debug 模式。然后看 config.py 里的数据库连接字符串这是最容易出问题的地方。常见的配置格式是# config.py 典型配置 SQLALCHEMY_DATABASE_URI mysqlpymysql://root:passwordlocalhost:3306/pharmacy_db SQLALCHEMY_TRACK_MODIFICATIONS False SECRET_KEY your-secret-key-heremysqlpymysql表示用 pymysql 驱动连接 MySQLroot:password是数据库用户名和密码localhost:3306是地址和端口pharmacy_db是数据库名。如果你本地没装 MySQL可以改成 SQLitesqlite:///pharmacy.db这样不需要额外安装数据库服务适合快速验证功能。但答辩时如果老师问数据库选型你要能说清楚 MySQL 和 SQLite 的区别——前者适合多用户并发、支持复杂查询和事务后者轻量但并发能力弱。2.3 数据库初始化与首次运行数据库配置改好之后需要建库建表。如果项目用了 Flask-Migrate执行迁移命令如果没有通常会有个init_db.py或 SQL 文件。手动建库的命令如下# 登录 MySQL mysql -u root -p # 创建数据库字符集用 utf8mb4 支持中文 CREATE DATABASE pharmacy_db CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; # 退出 exit;然后运行初始化脚本# 如果有迁移工具 flask db init flask db migrate -m init tables flask db upgrade # 如果有 SQL 文件 mysql -u root -p pharmacy_db init.sql # 直接启动应用 python app.py启动后浏览器访问http://127.0.0.1:5000能看到登录页就说明基本跑通了。如果报ModuleNotFoundError缺什么装什么如果报Access denied for user检查数据库密码如果报Unknown database说明库没建成功。这几个错误覆盖了首次运行 80% 的问题。3. 药品管理核心模块数据模型与增删改查实现3.1 药品表、库存表、销售表的设计逻辑药店药品管理系统的核心在于数据模型设计。很多同学拿到源码后只关心页面能不能跑但答辩时老师最可能问的就是「你的表结构为什么这么设计」。一个合理的模型至少包含三张核心表药品信息表、库存表、销售记录表。药品信息表存药品的基础属性库存表存当前数量和效期销售表存每一笔出库记录。用 SQLAlchemy 定义模型的典型写法from flask_sqlalchemy import SQLAlchemy from datetime import datetime db SQLAlchemy() class Drug(db.Model): 药品基础信息表 __tablename__ drug id db.Column(db.Integer, primary_keyTrue) name db.Column(db.String(100), nullableFalse, indexTrue) # 药品名称 category db.Column(db.String(50)) # 分类处方药/非处方药 spec db.Column(db.String(50)) # 规格 manufacturer db.Column(db.String(100)) # 生产厂家 price db.Column(db.Numeric(10, 2), nullableFalse) # 单价 created_at db.Column(db.DateTime, defaultdatetime.now) class Stock(db.Model): 库存表与药品一对一 __tablename__ stock id db.Column(db.Integer, primary_keyTrue) drug_id db.Column(db.Integer, db.ForeignKey(drug.id), uniqueTrue) quantity db.Column(db.Integer, default0) # 当前库存 expire_date db.Column(db.Date) # 有效期 warn_threshold db.Column(db.Integer, default10) # 预警阈值 updated_at db.Column(db.DateTime, defaultdatetime.now, onupdatedatetime.now) class Sale(db.Model): 销售记录表 __tablename__ sale id db.Column(db.Integer, primary_keyTrue) drug_id db.Column(db.Integer, db.ForeignKey(drug.id)) quantity db.Column(db.Integer, nullableFalse) total_price db.Column(db.Numeric(10, 2)) sale_time db.Column(db.DateTime, defaultdatetime.now) operator db.Column(db.String(50)) # 操作员这里有几个设计决策需要理解。price用Numeric(10, 2)而不是Float是因为金额计算不能用浮点数否则会出现0.1 0.2 ! 0.3的问题。drug_id在 Stock 表里加了uniqueTrue表示一个药品只对应一条库存记录。warn_threshold是预警阈值低于这个值就触发提醒这是药店系统里很实用的一个功能点。indexTrue加在药品名称上因为按名称搜索是最高频的查询。3.2 药品入库与库存更新的代码实现入库操作不是简单地在库存表上加一个数字它涉及事务先插入或更新库存再记录入库日志。如果两步之间出错数据就不一致了。下面是一个带事务控制的入库接口from flask import request, jsonify from sqlalchemy import exc app.route(/api/stock/in, methods[POST]) def stock_in(): 药品入库接口 data request.get_json() drug_id data.get(drug_id) quantity data.get(quantity, 0) expire_date data.get(expire_date) # 参数校验 if not drug_id or quantity 0: return jsonify({code: 400, msg: 参数不合法}), 400 try: stock Stock.query.filter_by(drug_iddrug_id).first() if stock: # 已有库存记录累加数量 stock.quantity quantity stock.expire_date expire_date or stock.expire_date else: # 首次入库新建记录 stock Stock(drug_iddrug_id, quantityquantity, expire_dateexpire_date) db.session.add(stock) db.session.commit() return jsonify({code: 200, msg: 入库成功, quantity: stock.quantity}) except exc.SQLAlchemyError as e: db.session.rollback() return jsonify({code: 500, msg: f入库失败: {str(e)}}), 500这段代码的关键点在于db.session.commit()和db.session.rollback()的配合。commit 之前的所有操作都在事务里一旦任何一步抛异常rollback 会把整个事务回滚保证库存不会出现「加了一半」的情况。参数校验放在最前面避免脏数据进入数据库。返回体里带上最新的库存数量前端可以直接更新显示不用再发一次查询请求。3.3 效期预警与库存查询的 SQL 优化效期预警是药店系统和普通进销存系统拉开差距的地方。实现逻辑是查询所有expire_date在当前时间之后 30 天内的药品以及quantity低于warn_threshold的记录。用 SQLAlchemy 写出来是这样from datetime import date, timedelta def get_warnings(): 获取效期预警和低库存预警 today date.today() thirty_days_later today timedelta(days30) # 效期预警30天内过期 expire_warnings db.session.query(Drug, Stock).join( Stock, Drug.id Stock.drug_id ).filter( Stock.expire_date thirty_days_later, Stock.expire_date today ).all() # 低库存预警 low_stock db.session.query(Drug, Stock).join( Stock, Drug.id Stock.drug_id ).filter( Stock.quantity Stock.warn_threshold ).all() return { expire: [{name: d.name, expire_date: str(s.expire_date)} for d, s in expire_warnings], low_stock: [{name: d.name, quantity: s.quantity} for d, s in low_stock] }如果数据量大了比如药品有几千种这个查询会变慢。优化方向是在expire_date和quantity上建索引或者用定时任务提前算好预警结果缓存起来。教学项目里数据量小直接查没问题但答辩时你可以主动提一句「如果数据量增长我会在 expire_date 上加索引并把预警结果缓存到 Redis」这就是加分项。4. 避坑与排查跑药店管理系统时最容易翻车的 5 个地方4.1 中文乱码从数据库到页面的全链路排查现象药品名称在页面上显示成???或者测试。原因通常出在三个地方——数据库字符集不是 utf8mb4、连接字符串没指定编码、或者 HTML 页面没声明 charset。解决顺序是先确认建库时用了CHARACTER SET utf8mb4再检查连接字符串有没有?charsetutf8mb4最后看模板文件头部有没有meta charsetUTF-8。三个都对了中文就不会出问题。4.2 外键约束报错Cannot add or update a child row现象插入销售记录时报IntegrityError: Cannot add or update a child row。原因是drug_id在药品表里不存在外键约束拦住了。解决方法是插入前先查一下药品是否存在或者在前端做下拉选择而不是手动输入 ID。如果确实需要灵活处理可以在模型里把外键的ondelete设为SET NULL但不建议在教学项目里这么做因为会掩盖数据不一致的问题。4.3 端口占用与 debug 模式的安全隐患现象启动时报OSError: [Errno 98] Address already in use。原因是 5000 端口被占用了可能是上次的进程没退干净也可能是 macOS 的 AirPlay 占用了 5000。解决办法是换端口app.run(port5001)或者用lsof -i:5000找到进程 kill 掉。另外debugTrue只应该在开发时开部署到服务器上必须关掉否则会暴露源码和堆栈信息。4.4 依赖版本冲突Flask 2.x 和 3.x 的 API 差异现象源码在别人机器上能跑到你这里报ImportError: cannot import name url_quote from werkzeug。原因是 Flask 2.x 和 3.x 对 Werkzeug 的依赖版本不同装错了版本就会报这个错。解决办法是看 requirements.txt 里有没有锁版本没有的话手动降级pip install werkzeug2.3.7。这类问题在基于 Python 的毕业设计里非常常见本质是教学项目往往没有严格锁定依赖。4.5 数据库连接池耗尽Too many connections现象系统跑一段时间后报Too many connections。原因是每次请求都新建了数据库连接没有释放。Flask-SQLAlchemy 默认会用连接池但如果你在代码里手动pymysql.connect()而忘了 close就会泄漏。解决办法是统一用 ORM 的 session或者在手动连接时用with语句确保释放。教学项目并发低这个问题不容易暴露但答辩时如果老师问「你的系统能支持多少并发」你要能说出连接池的概念。5. 从能跑到能讲答辩演示与二次开发的进阶技巧5.1 准备一套可复现的演示数据答辩时最尴尬的情况是系统跑起来了但没数据页面空空荡荡。提前准备一份种子数据脚本包含 20 到 30 种常见药品、对应的库存记录和几条销售记录。用 Python 脚本批量插入比手动敲 SQL 快得多# seed_data.py 种子数据脚本 import random from datetime import date, timedelta from app import app, db from models import Drug, Stock, Sale drugs [ (阿莫西林胶囊, 处方药, 0.25g*24粒, 华北制药, 18.50), (布洛芬缓释胶囊, 非处方药, 0.3g*20粒, 中美史克, 22.00), (维生素C片, 非处方药, 100mg*100片, 石药集团, 8.00), # ... 更多药品 ] with app.app_context(): db.create_all() for name, cat, spec, mfr, price in drugs: drug Drug(namename, categorycat, specspec, manufacturermfr, priceprice) db.session.add(drug) db.session.flush() # 拿到 drug.id stock Stock(drug_iddrug.id, quantityrandom.randint(5, 200), expire_datedate.today() timedelta(daysrandom.randint(10, 700)), warn_threshold20) db.session.add(stock) db.session.commit() print(种子数据插入完成)db.session.flush()的作用是把当前对象刷到数据库但不提交事务这样能拿到自增的drug.id用于关联库存记录。跑完这个脚本系统里就有数据可以演示了。5.2 答辩时怎么把 CRUD 讲出技术含量很多同学答辩时只会说「这是增删改查」老师一听就觉得没深度。同样的代码换一种讲法效果完全不同。比如入库接口你可以这样讲这个接口用了数据库事务保证库存和日志的一致性参数校验在前端和后端各做一次防止脏数据返回体里带了最新库存减少一次查询。再比如效期预警你可以说这里用了联合查询加时间范围过滤如果数据量增长会在 expire_date 上建索引进一步可以用定时任务预计算。把「做了什么」升级成「为什么这么做、还能怎么优化」答辩分数会明显不一样。5.3 二次开发方向从课程设计到简历项目如果想让这个项目在简历上更有竞争力可以往三个方向扩展。第一是加权限控制用 Flask-Login 做基于角色的访问控制管理员能管理药品和用户普通员工只能做销售和查询。第二是加数据可视化用 ECharts 或 Pyecharts 做一个销售趋势图和库存分布图答辩时视觉效果很好。第三是加导出功能把销售记录导出成 Excel用 openpyxl 或 pandas 都能实现。这三个方向都不需要推翻现有代码在原有结构上加模块就行适合在课程设计验收前快速迭代。我自己做这类项目最大的教训是不要一上来就追求功能多先把一条主流程跑通——登录、查药品、入库、销售、看库存——这条线通了其他功能都是锦上添花。很多同学卡在环境配置和数据库连接上耗了一周最后没时间做核心功能答辩时只能演示一个登录页。先把最小闭环跑起来再逐步加功能这个节奏最稳。希望帮到你。本文还有配套的精品资源点击获取