Python药品管理系统毕设:Flask+SQLite从数据库设计到库存预警全解析

发布时间:2026/10/11 8:27:50

Python药品管理系统毕设:Flask+SQLite从数据库设计到库存预警全解析 有人找我打白工说是在网上买了一套“基于Python的药品管理系统毕设源码”结果打开发现各种报错登录页长什么样都不知道让我帮忙看看。我扫了一眼发现问题不太在别人身上——这类所谓“毕设源码”八九成是拿旧项目的改了个名数据库表结构、路由命名、前端模板全对不上号跑不起来是常态。但话说回来药品管理系统确实是Python方向里最稳妥的毕设选题之一业务逻辑清晰、数据库设计有讲头、页面做出来也好看答辩时不愁没东西讲。我干脆借这个机会把一套能真正跑通、能改、能讲的Python药品管理系统方案从头到尾拆一遍从技术选型到数据库设计从核心功能到常见坑位全部放出来给准备拿这个题目做毕设的同学一条能直接走的路。这套方案用的是Flask SQLite Bootstrap Chart.js的技术组合所有代码都能在本地跑起来全程不需要付费工具也不需要特别高配的电脑。对新手来说它能让你完整看到“网页应用到底是怎么从零搭建起来的”对准备参加答辩的同学来说它把用户登录、权限区分、药品CRUD、模糊搜索、库存预警、可视化报表这些必考功能全部覆盖到了。我不灌水直接按我实际带学生做这个项目的顺序来讲。1. 系统设计思路与整体架构拆解1.1 为什么选Python做药品管理系统很多同学纠结毕设到底选Java还是Python。Java体系Spring Boot Vue确实是企业级标配但问题是你如果之前没怎么接触过Java光是把开发环境跑起来就要折腾好几天更别提Spring那套依赖注入、配置中心、Bean生命周期对基础薄弱的同学很不友好。Python不一样的地方在于它开发效率高写个Web应用需要的代码量比Java少一半以上而且Flask这个框架特别适合用来理解Web应用的本质——路由、请求、响应、模板渲染、数据库交互每个概念都清清楚楚没有中间那一堆“魔法”。对于毕设来说导师看重的不是你用了多新的技术栈而是你能不能讲清楚“这个系统为什么要这么设计”“数据是怎么流转的”Python恰好在这方面最通人性。再加上骨架好搭、模块相对成熟、修改成本低。哪怕是最后几天临时加个功能在Flask里面也就是加个路由、加个函数、加个模板的事不会像SSM项目那样改一处动全身。选Python做药品管理系统性价比相当高。1.2 Flask SQLite毕设项目的黄金组合很多人一听到数据库就想到MySQL觉得不用MySQL就不专业。这个想法可以理解但对毕设来说SQLite反而是更合理的选择。SQLite是嵌入式数据库数据就存在一个文件里不需要安装数据库服务不需要配置端口不需要记账户密码。你用Flask连接它一行代码就够了。整个项目拷到U盘里就能带走到答辩现场的电脑上解压、装依赖、运行三步就能演示完全不用处理“MySQL服务没启动”“root密码忘了”这类和环境相关的破事。务实一点说答辩的核心是“你的系统能跑”而不是“你用了什么数据库引擎”。如果你的学校强制要求用MySQL那也很简单只需要把连接字符串换成MySQL的再用SQLAlchemy把建表语句跑一遍其余代码完全不用动。我在后面会专门讲怎么迁移。前端方面我建议直接用Bootstrap不需要花时间自己写CSS。Bootstrap自带的表格样式、按钮风格、表单组件长得规规矩矩一个毕设系统完全够用。图表的话用Chart.js是一个CDN引进来就能用的JS图表库饼图、柱状图、折线图都有不需要装什么Node环境省心得很。1.3 功能模块与角色权限的整体划分药品管理系统的功能看着多本质上是围绕“药品生命周期”来划分的采购进来、库存放着、销售出库、库存少了要补货、过期了要处理再加上操作用户管理。我设计的角色分两种管理员admin和普通员工user。管理员能看到全部菜单包括员工管理、进货入库、出库记录、数据报表普通员工只能看到药品列表、库存查询、出库登记这些日常操作页面。这个权限区分很重要不是为了炫技而是体现了一个基本的系统设计思想不同角色看到不同内容承担不同职责。系统里面还要有药品档案管理、药品入库/出库管理、库存预警管理。这四块是核心再加一个好看的登录页、一个仪表盘毕设第一阶段的工作就圆满了。我建议你不要一开始就给自己挖太多功能坑例如什么药品批号效期追踪、供应商对账、采购审批流这些大而全的功能在毕设里反而是包袱——演示时讲不深入实现时bug一大堆还会拖慢核心功能的完成度。2. 数据库设计四张表撑起整个系统2.1 核心表结构与字段设计数据库是整个系统的地基。很多同学喜欢一开始就把字段建得特别多特别细后面写代码的时候就发现改表结构比写代码麻烦得多。我的建议是最开始只建四张表宁可字段少一点也要关系清楚。第一张表用户表users。字段包括id、username、password_hash、role。username唯一password_hash存的是Werkzeug生成的哈希值千万不要明文存密码。role默认填user手动给一个用户改成admin作为管理员。第二张表药品表medicines。字段id、name、category分类、specification规格例如“0.25g*24片”、unit单位例如“盒”、purchase_price进价、sale_price售价、stock库存数量、min_stock库存预警下限、expire_date有效期、supplier供应商。第三张表入库记录表inbound_records。字段id、medicine_id外键关联药品表、quantity、supplier、inbound_date、operator操作人。每一条记录意味着某个药品进了一批货同时要把药品表的stock加上相应数量这个操作要在同一个事务里完成防止数据只读了一半。第四张表出库记录表outbound_records。字段类似id、medicine_id、quantity、customer购买方、outbound_date、operator。对应的药品stock要相应减少。建表我直接用原生SQL用SQLite的命令行、或者Python脚本里的sqlite3模块来建。好处是你能看得懂每一行在干什么。如果你用Flask-SQLAlchemy的ORM方式也可以但前提是你要能跟导师讲明白ORM和SQL的关系。2.2 给演示数据“造”出真实感一个空表跑起来的系统没有任何说服力你得给系统填充演示数据。这里有个实操技巧不要手动一条条插入写一个Python脚本一次性生成几十条有逻辑关系的数据。药品名要真实像“阿莫西林胶囊”“布洛芬缓释片”“藿香正气口服液”这种别整“测试药品001”。库存量也要有讲究大部分药品设成正常库存例如500盒挑五六个药品故意设成低于预警线的数量例如只剩8盒这样你演示库存预警功能的时候才能直接看到效果。入库记录的时间要分布在过去半年出库记录要分布在最近30天每天有2到5条不等的售卖记录。用随机数生成同时在脚本里面设定随机种子保证每次跑出来的数据基本稳定。这种细节不会有人夸你但答辩的时候老师问你“为什么这个药库存告警了”你能直接回应“因为最近一个月它的出库量持续大于入库量”这就已经超出了普通学生的水平。2.3 登录会话与权限校验的底层逻辑登录的状态管理我用Flask-Login这个扩展它专门帮你把“用户登录状态”保存在session里面。你不需要自己处理cookie和session到底怎么协同只需要记住两点登录成功后调用login_user(user)退出时调用logout_user()需要登录才能访问的页面在路由前面加login_required这个装饰器。权限校验我是自己写的装饰器叫admin_required。在Flask里装饰器的写法并不复杂它的本质就是“检查当前登录用户的角色是admin就放行不是就返回403页面”。不要觉得装饰器很高级它其实就是把一个函数包装成另一个函数在进入真正的视图函数之前先干一些事情。这非常值得你花半小时看懂因为它是一个贯穿“权限控制”的经典用法。我顺手给你看一下登录路由最核心的验证逻辑from werkzeug.security import check_password_hash from flask_login import login_user, login_required, logout_user, current_user app.route(/login, methods[GET, POST]) def login(): if request.method POST: username request.form.get(username) password request.form.get(password) user User.query.filter_by(usernameusername).first() if user and check_password_hash(user.password_hash, password): login_user(user) return redirect(url_for(dashboard)) flash(用户名或密码错误, danger) return render_template(login.html)这里有两个细节值得你思考为什么每次判断密码用的是check_password_hash而不是直接比较字符串因为数据库里存的是哈希值你不能拿明文密码去跟哈希值比。为什么要用filter_by().first()而不是filter_by().all()因为用户名唯一取第一条就够了。3. 核心功能实现从空白页面到能演示的完整系统3.1 药品的增删改查把CRUD写出干净的层次感药品管理是系统的基础模块无非就是“列表展示、新增药品、修改药品、删除药品”。列表页我建议做成一个带搜索框的表格页面。表格每一行对应一条药品记录列显示名称、分类、规格、售价、库存、状态。状态这一列不用存进数据库而是根据stock和min_stock动态计算出来的如果stock小于min_stock状态显示“库存不足”背景标红如果stock大于等于min_stock显示“正常”。这样做的好处是你不用额外维护一个状态字段省掉同步问题。新增和修改可以用同一个页面模板通过传入的medicine_id来判断是编辑还是新增。如果是编辑把已有的药品数据填入表单如果是新增表单留空。在Flask里这只需要在视图函数里判断一下有没有传入id参数然后渲染同一个模板。模板里面自己写个判断决定是提交到add_medicine还是update_medicine的路由。这样你的代码量直接减半逻辑还好维护。删除功能要慎重我建议做“软删除”而不是“硬删除”。硬删除是直接把记录从表里DELETE掉软删除是给表加一个is_deleted字段删除的时候只是把这个字段置为1。为什么因为药品一旦有入库出库记录直接删掉会导致历史记录找不到对应药品外键关系断掉演示的时候非常尴尬。软删除虽然多写几行代码但是你的数据完整性有保障老师查历史记录的时候也能看清你的设计思想。3.2 模糊搜索与分页让数据量大时页面依旧可用药品列表的搜索说白了就是一个SQL的LIKE查询加一个分页逻辑。search request.args.get(search, , typestr) page request.args.get(page, 1, typeint) query Medicine.query if search: query query.filter( db.or_( Medicine.name.like(f%{search}%), Medicine.category.like(f%{search}%), Medicine.supplier.like(f%{search}%) ) ) paginated query.paginate(pagepage, per_page10) return render_template(medicine_list.html, paginationpaginated, searchsearch)注意search从request.args取的时候要带默认值不然首次访问没带参数会报错。分页用paginate方法它会返回一个Pagination对象你只要在模板里面用pagination.items、pagination.pages、pagination.has_prev、pagination.has_next这些属性就能遍历数据和控制上一页下一页的显示。这是Flask-SQLAlchemy给的美妙甜品别自己手动去算offset和limit除非你想在答辩的时候自找麻烦。3.3 库存预警让报表自己会说话药品库存预警是这个系统里最容易被夸“有想法的功能”。实现逻辑特别朴素在首页仪表盘上用以下几个统计卡片药品总数、库存不足数量、今日入库总数量、今日出库总数量。其中库存不足数量就是查一下有多少药品的stock min_stock。但光有一个数字不够我习惯在页面上放一个“库存预警表格”把所有库存低于下限的药品按“缺口率”min_stock减去stock除以min_stock从高到低排出来。这个排名式预警能把老师注意力引到“怎么在系统里做决策参考”这个点上而不是仅仅停留在“数据展示”。你可以再加一句口头讲解“这样系统不只是报警还能给采购建议”。对应的SQL查询逻辑很简单用OrderBy即可warning_items Medicine.query.filter( Medicine.stock Medicine.min_stock ).order_by((Medicine.min_stock - Medicine.stock).desc()).all()3.4 数据可视化不用写前端也能做出专业仪表盘仪表盘是答辩的“门面”。我的方案是后端通过一个JSON接口返回统计数据前端用Chart.js生成图表两块数据一拼一个好看的仪表盘就出来了。具体来说我做了三个图。第一个是近14天药品出库数量柱状图数据从出库记录表按日期聚合出来SELECT date(outbound_date) as d, SUM(quantity) as total FROM outbound_records GROUP BY date(outbound_date)拿到的数组往前端传。第二个是药品分类占比饼图按分类统计药品品种数。第三个是库存量前十的药品横向条形图一眼就能看出哪些药库存积压了。这里有个关键实现细节给图表模板返回数据时建议直接返回JSON再由页面用fetch请求来取。这样你以后想换个图表库或者扩展成ECharts都能无缝切换。不要用模板渲染里硬塞数据的方式因为那会限制图表的灵活性。3.5 入库与出库事务处理防止数据不一致入库和出库操作是业务闭环上最关键的环节。比如入库前端表单让你选药品、填数量、填供应商提交后后端做两件事往inbound_records表插入记录同时把medicines表对应药品的库存加数量。这两件事必须是一体的要么都成功要么都失败。反过来出库也要保持一致出库记录写入库存相应减掉。如果没有事务控制假设库存加成功了而记录插入失败你的账就会对不上。演示的时候可能看不出问题但写代码的时候养成“涉及多表更新就用事务”的习惯是对自己的保护。在Flask-SQLAlchemy里事务很好处理用完commit就完事了如果中间出现异常就rollback。具体代码如下try: new_record InboundRecord(medicine_idmid, quantityquantity, suppliersupplier) db.session.add(new_record) medicine.stock medicine.stock quantity db.session.commit() except Exception as e: db.session.rollback() flash(f入库失败: {str(e)}, danger) return redirect(url_for(inbound_add))下单的时候还有一道防线出库数量不能大于当前库存。你可能会觉得“我不写这个判断不就好了”但我可以告诉你答辩现场老师一定会在你操作台上手滑输一个超过库存的数字到时候页面上全是英文报错那场面真的没法收场。所以优先写清楚库存校验返回给用户的也应该是中文提示。3.6 导出报表一个Excel送分项功能如果做完上面的功能还有余力我给一个非常建议添加的功能把出库记录按时间段导出成Excel。实现办法很简单用pandas或者openpyxl把查出来的记录写进Excel文件然后通过Flask的send_file方法让浏览器下载。整个功能代码不超过30行但答辩的效果明显更好——你可以演示“近7天的销售数据导出给财务”这像是一个真实可落地的业务场景把你从“写了一个作业”直接拉到“做了一个系统”的位置上。4. 实操中常见的坑与排查方案4.1 跑不起来先按这个顺序排查我第一次给我带的学生跑这套系统时几乎每个人都有不同版本的报错。我把最高频的几个问题整理出来。首先是依赖装完但run时报ModuleNotFoundError。这通常是本机Python多个版本混装导致。Flask应用应该用虚拟环境在项目根目录执行python -m venv venv venv\Scripts\activate # Windows source venv/bin/activate # macOS / Linux pip install flask flask-sqlalchemy flask-login激活虚拟环境后再运行python app.py基本就不存在flask跑错环境的问题。第二个高危点是数据库文件没有生成。很多人写的connect不是在create_all()之前执行的导致表一直没建成功。建立数据库表的动作要放在所有模型定义完之后再调用db.create_all()。这个动作建议放在run之前单独用一个init_db()函数做别把它塞到路由里面去。4.2 数据中文乱码和时区问题SQLite里读中文其实没有问题不显示的根因往往是浏览器页面没设置UTF-8。在Flask的模板里加一句 基本能解决。关于日期时区问题要区分入库时间跟显示时间。如果你是用datetime.now()去存的而服务器和本地时间有时差建议项目里统一用本地时间显示保证演示的时候日期跟当前时间是对得上的。千万别小看这种小事。我见过答辩现场学生演示销售数据的时候老师随手点了个“按日期筛选”结果页面弹出来“2016-01-01”这种陈年旧时间气氛瞬间凝固。所以造数据的脚本把日期往最近一个月生成问题就迎刃而解了。4.3 表单提交后CSRF报错与端口占用如果用了Flask-WTF的表单默认开启了CSRF保护你只要没有在模板里加csrf_token提交就一定报错。这里有个建议既然是毕设系统不要关闭CSRF保护而是学会在模板里加input typehidden namecsrf_token value{{ csrf_token() }}这是正确的做法也方便你在答辩里讲安全措施。端口占用的处理Windows下是netstat -ano | findstr 5000然后taskkill /F /PID 。macOS或Linux下是lsof -i :5000然后kill -9 。别总是重启电脑这个方法背下来能帮你节省大量时间。4.4 登录后页面没有权限提示一个经典逻辑错误是登录完直接跳到首页不判断角色。管理员登录进去给普通员工界面或者反过来。解决方案就是在每个需要管理员权限的路由上加上admin_required装饰器同时在模板里根据current_user.role控制菜单项的显示。这个“前端控制显示后端控制权限”的双层设计是你答辩时可以主动讲的亮点。4.5 答辩前一定要准备的20个“为什么”最后给你一份经验清单是我看过无数毕业论文答辩总结出来的高频追问点为什么密码要哈希存储而不是加密存储为什么用Session管理登录状态而不是把用户名存Cookie为什么入库出库要在一个事务里完成药品库存预警的阈值是怎么定的如果并发两个人同时出库库存会不会变负数如果删除一个有过业务记录的药品会出现什么问题为什么分页查询比一次性查询所有记录更优如果药品数量达到十万条这套系统能不能撑住这八个问题每一个你都得能在不看代码的情况下用自己的话说清楚。很多同学代码是能跑的但讲不出设计理由最后被扣分真的很亏。我自己的经验是你不需要把系统做得多“大”但要能自圆其说“为什么这个数据要这样设计”“为什么这里要用这个函数”“并发情况下这里会有什么问题”。老师要考察的不是你写出了一堆功能而是你真正理解了你写出来的东西。5. 项目目录结构与运行流程参考为了让整个方案更完整我放一个推荐的项目目录结构你可以拿它当骨架去改pharmacy/ ├── app.py # 应用入口创建Flask实例注册蓝图 ├── models.py # 数据库模型类 ├── forms.py # Flask-WTF表单类如果用 ├── views/ │ ├── auth.py # 登录登出路由 │ ├── medicine.py # 药品管理路由 │ ├── inventory.py # 入库出库路由 │ └── dashboard.py # 仪表盘与图表接口 ├── templates/ │ ├── base.html # 母版页 │ ├── login.html │ ├── dashboard.html │ ├── medicine_list.html │ └── ... ├── static/ │ ├── css/ │ ├── js/ # 放Chart.js相关的页面内脚本 ├── requirements.txt └── init_db.py # 初始化数据库和造演示数据脚本如果你按这个目录去组织一个H2的功能模块就一个Python文件特别清晰。答辩老师问你“登录功能在哪写的”你直接指views/auth.py他问“药品怎么入库的”你指views/inventory.py这种条理性本身就是加分项。运行流程也给你写清楚cd pharmacy python -m venv venv venv\Scripts\activate pip install -r requirements.txt python init_db.py python app.py然后在浏览器打开http://127.0.0.1:5000用init_db.py里面预设的admin/admin123登录。整个过程不依赖任何外部服务所有数据都在本地文件里演示的时候只要带一台笔记本就够。最后说一句我自己带毕设几年下来的感慨药品管理系统这个题目难不在技术而在你能不能沉下心把每一条数据流程走通。你今天的精力花在哪里答辩那天的底气就在哪里。把这套结构吃透了改造成别的进销存系统、图书管理系统、学生选课系统都不过是模型换名、字段替换的事。
延伸阅读

更多相关文章

2026/10/11 8:27:50

如何用一维助手创建渠道活码?完整操作手册

投了5个渠道广告,加了500个客户。 问:哪个渠道效果最好? 答:不知道,都是同一个二维码。 没有渠道活码,投放效果全靠猜。 一、什么是渠道活码? 一句话: 一个入口,多个渠道…

2026/10/11 8:27:50

结课项目2026全流程指南:从选题到答辩的实操攻略

很多同学是到学期最后两周才发现自己还有一个结课项目没动,于是连续熬几个通宵,交上去一个连自己都讲不清楚的东西。这个场景我见过太多次了。“结课项目2026”这个标题看起来只是一个平平淡淡的课程任务标签,但它其实传递了一个很重要的信号…

2026/10/11 8:27:50

【APP安全测试】抓包环境搭建

一、下载靶场 apk 下载 apk 靶场:https://www.shangsec.com/#/ 下载好后,直接拖入 mumu 模拟器安装 二、安卓设备配置 需要开启这两个设置的原因如下: Root 相当于拿到 Android 设备的超级管理员权限,渗透测试 App 时&#xff…

2026/10/11 9:32:54

素材自动变大纲一键出片——一次做 PPT 流程的工程化尝试

## 背景做 PPT 找模板排版到半夜是很多团队都遇到过的老问题。## 核心能力- 文本素材自动梳理大纲- 大纲支持二次编辑- 多套配色模板可选- 渲染16:9标准PPTX文件## 落地场景日常办公等场景都能直接搬进工作流,输入是散乱的原始材料,输出是可直接交付的成…

2026/10/11 9:32:54

城市生命线无人机智能巡检:选型、技术路线与落地实践

城市生命线这个词,听起来有点宏大,其实就是我们每天都离不开的燃气管道、供水管网、供电线路、桥梁隧道,还有地下排水系统。前几年做这类基础设施巡检,主要靠人跑、靠眼看、靠笔记,效率低不说,很多隐蔽缺陷…

2026/10/11 9:32:54

基于深度学习的恶意代码检测系统实战:PE特征提取与PyTorch模型部署

简介:这是一套面向高校学生与人工智能初学者的深度学习实战项目包,以恶意代码检测为主题,适合用作毕业设计、课程设计或期末大作业的完整参考方案。资源围绕数据预处理、特征提取、模型训练与结果输出等环节展开,涉及CNN、RNN、LS…

2026/10/11 9:32:54

连续投影算法SPA实战:光谱变量选择与Python实现详解

简介:连续投影算法(SPA)光谱分析资料,面向从事光谱数据处理与特征波长筛选的科研人员和学习者,用于解决高维光谱数据冗余、过拟合及分类模型复杂化等问题,常与主成分分析结合实现有效降维。压缩包共11个文件…

2026/10/11 9:27:53

等保 2.0 入门解读,测评流程与常见整改项

等保 2.0 入门解读,测评流程与常见整改项免责声明:本文仅用于网络安全、等保合规知识学习,所有内容仅作科普参考。等保定级、备案、测评、整改工作需要由持证专业人员、具备资质的第三方测评机构实施。任何单位开展等级保护工作,必…

2026/10/11 0:02:13

Python调用Gemini Structured Outputs实现工单路由门禁

客服工单最怕的不是模型“答错一句话”,而是它给出一段看起来合理的说明,程序却从中猜错优先级。通俗做法是:要求模型只交 JSON(JavaScript Object Notation,轻量数据格式),再让代码验证它。Gem…

2026/10/11 0:02:13

Spring Boot超市进销存系统毕设实战:从需求拆解到答辩通关

最近带的一个学生项目组里,有A同学跑来问我:选什么毕设题目最稳妥,既能让评审老师觉得工作量够,又不会在答辩时被问到语无伦次。我第一反应就是推荐基于Spring Boot的超市仓库管理系统——也就是超市进销存系统。这个题目乍一看平…

2026/10/11 0:02:13

Flutter StatefulWidget 生命周期核心解析

很多刚开始接触 Flutter 的朋友,在看完一堆“Hello World”和基础组件之后,大概率都会撞上同一堵墙:StatefulWidget 里那堆 initState、build、dispose 方法,到底什么时候被调用?为什么顺序是那样?在里面到…

2026/10/11 0:02:13

Python调用Gemini Structured Outputs实现工单路由门禁

客服工单最怕的不是模型“答错一句话”,而是它给出一段看起来合理的说明,程序却从中猜错优先级。通俗做法是:要求模型只交 JSON(JavaScript Object Notation,轻量数据格式),再让代码验证它。Gem…

2026/10/11 0:02:13

Spring Boot超市进销存系统毕设实战:从需求拆解到答辩通关

最近带的一个学生项目组里,有A同学跑来问我:选什么毕设题目最稳妥,既能让评审老师觉得工作量够,又不会在答辩时被问到语无伦次。我第一反应就是推荐基于Spring Boot的超市仓库管理系统——也就是超市进销存系统。这个题目乍一看平…

2026/10/11 0:02:13

Flutter StatefulWidget 生命周期核心解析

很多刚开始接触 Flutter 的朋友,在看完一堆“Hello World”和基础组件之后,大概率都会撞上同一堵墙:StatefulWidget 里那堆 initState、build、dispose 方法,到底什么时候被调用?为什么顺序是那样?在里面到…

还想了解更多?直接咨询顾问

免费诊断 + 免费方案 + 透明报价。

全国咨询热线400-8866-253
免费获取方案
☎咨询二维码 ☎ ↑