资讯动态

Python Flask医院信息管理系统:从工程结构到部署避坑指南

发布时间:2026/10/18 12:01:01 来源:尧图企业网站定制
简介基于Python实现的医院信息管理系统完整项目面向Python Web入门学习者、计算机相关专业学生及医疗信息化方向开发人员围绕门诊、住院、收费、家庭医生等业务流程提供了一套可运行的项目框架。资源包共93个文件、76KB涵盖52个HTML页面模板、31个Python源码文件、SQL初始化脚本以及配置与迁移文件等前端页面、后端逻辑与数据库结构均清晰可分。目前已有2118人浏览学习适合作为课程设计、毕业设计或业务系统开发的参考范式。项目采用类似MVC的模块化结构涉及数据库建模、ORM迁移、路由视图设计、模板渲染及数据初始化等完整环节配合README和requirements.txt可快速理解依赖安装与启动方式。阅读源码可获得从项目骨架搭建到具体业务落地的实操经验尤其适合希望掌握Web框架与数据库联动开发的学习者。1. 基于python实现医院信息管理系统先把整套工程跑起来再研究细节医疗行业的信息系统有个共同特点业务模块多、权限要求高而且数据一旦录入就要保证可追溯。这套基于python实现医院信息管理系统是典型的 Flask 多蓝图工程把门诊、住院、划价、家庭医生、认证授权拆成了独立 app配合 Alembic 管理数据库迁移fakeinfo.sql 还带了可直接导入的演示数据。它解决的核心问题是“一个人能不能快速复现一套医院业务流程”而不是停留在单表增删改查的玩具层次。适合正在学 Flask、想做课设或接手这类小成品工程的从业者。我拿到源码后不猜架构先从入口把服务拉起来。2. 工程结构先拆一遍入口、配置、数据库迁移各自承担什么角色2.1 目录与蓝图为什么医院系统要拆成多个独立业务模块拿到压缩包后先看顶层目录我一般习惯把所有文件和目录摊开确认哪些是框架生成的、哪些是业务代码。这套资源的核心目录长这样HospitalManagement-master/ ├── app/ │ ├── outpatient/ # 门诊业务 │ ├── inpatient/ # 住院业务 │ ├── charges/ # 划价收费 │ ├── familydoctor/ # 家庭医生 │ ├── auth/ # 登录认证 │ ├── templates/ # Jinja2 模板 │ ├── static/ # 静态资源 │ ├── __init__.py │ ├── decorator.py │ ├── model.py │ └── config.py ├── migrations/ # Alembic 迁移脚本 ├── fakeinfo.sql ├── run.py ├── manage.py ├── requirements.txt └── alembic.ini这里最值得注意的不是app文件本身而是auth、outpatient、inpatient、charges、familydoctor五个业务目录。如果把所有路由堆在app.py里门诊加一个功能就要翻几百行代码按业务域拆成蓝图每个模块的views.py只管自己的路由模板也能按模块放互不打扰。实际开发中我习惯把“认证”独立出来因为住院、门诊、划价都要判断当前用户是谁越通用的东西越要单独放。decorator.py一般放的是登录校验装饰器model.py放 SQLAlchemy ORM 模型config.py负责数据库连接和 Flask 配置。migrations目录是 Alembic 生成的用来跟踪表结构变更manage.py走 Flask-Migrate 命令行run.py则是最直接的启动入口。2.2 run.py 与 manage.py两种启动方式的分工很多新手会困惑run.py和manage.py都能启动吗功能不重复吗实际上分工挺明确。run.py是给本地开发用的启动一个开发服务器manage.py是给管理数据库迁移和初始化用的。先看run.py最常见的写法# run.py from app import create_app app create_app() if __name__ __main__: # 开发环境常用 5000 端口生产环境建议用 gunicorn 接管 app.run(host127.0.0.1, port5000, debugTrue)逻辑很简单从app/__init__.py里取工厂函数create_app然后把应用跑起来。debugTrue改成False之后报错不再暴露具体堆栈host127.0.0.1只允许本机访问如果想让局域网里的其他电脑联调可以改成0.0.0.0但要注意数据库账号别用弱密码。manage.py则是给 Flask-Script 或 Flask-Migrate 提供命令行入口典型的容器框架是这样# manage.py from flask_script import Manager from flask_migrate import Migrate, MigrateCommand from app import create_app, db app create_app() migrate Migrate(app, db) manager Manager(app) manager.add_command(db, MigrateCommand) if __name__ __main__: manager.run()这里的db命令是核心后续执行python manage.py db init、python manage.py db migrate、python manage.py db upgrade都靠它。不过 Python 生态迭代很快新版 Flask 推荐直接用内置flask db命令旧项目保留manage.py往往是为了兼容历史团队习惯跑的时候如果提示缺少flask_script装一下依赖就行。2.3 config.py 与 model.py数据库连接和 ORM 模型怎么配合数据库配置通常集中在config.py这个项目大概率用的是 MySQL开发环境也可能退回 SQLite。我一般会把配置写成可被环境变量覆盖的形式避免改代码才能换库# app/config.py import os BASE_DIR os.path.abspath(os.path.dirname(os.path.dirname(__file__))) class Config: SECRET_KEY os.environ.get(SECRET_KEY, dev-only-secret) SQLALCHEMY_DATABASE_URI os.environ.get( DATABASE_URL, mysqlpymysql://root:123456127.0.0.1:3306/hospital?charsetutf8mb4 ) SQLALCHEMY_TRACK_MODIFICATIONS False这里的mysqlpymysql://是 SQLAlchemy 的方言驱动写法用户名、密码、地址、端口、库名都用冒号和斜杠分开。charsetutf8mb4很关键医院系统里姓名、地址、医嘱都可能是中文用utf8mb4才能完整存下生僻字和 Emoji别图省事只写utf8很多血泪教训都是从这里开始的。model.py里是 ORM 模型比如用户表和患者表的关系常见片段像是# app/model.py from app import db from werkzeug.security import generate_password_hash, check_password_hash class User(db.Model): __tablename__ user id db.Column(db.Integer, primary_keyTrue) username db.Column(db.String(64), uniqueTrue, nullableFalse) password_hash db.Column(db.String(128), nullableFalse) role db.Column(db.String(32), defaultdoctor) def set_password(self, password): # 只存 hash不存明文 self.password_hash generate_password_hash(password) def check_password(self, password): return check_password_hash(self.password_hash, password)SQLAlchemy 把“Python 对象”和“数据库表”做了映射之后代码里写db.session.add(user)执行db.session.commit()底层就翻译成 INSERT 语句。配合 Alembic每次表结构改动都能生成迁移文件相当于给数据库上了后悔药。这也是整套工程能放一堆业务模块还不乱的原因模型层统一视图层各管各的。3. 把环境搭起来从 requirements.txt 到首次登录3.1 创建虚拟环境并安装依赖拿到资源的下一步一定是搭环境不要一根筋直接在全局 Python 里pip install否则依赖冲突能毁掉一晚上。我一般先在项目根目录下建虚拟环境cd HospitalManagement-master python -m venv venvWindows 下激活venv\Scripts\activatemacOS / Linux 下激活source venv/bin/activate激活之后命令行前缀会多出(venv)这代表当前用的是隔离环境。然后安装依赖pip install -r requirements.txt如果下载速度慢可以临时换国内镜像源比如清华或者阿里云的 PyPI 镜像但要注意不要把镜像配置写进项目里省得别人复现时踩坑。装完依赖后可以用pip list看一眼关键库版本Flask、SQLAlchemy、PyMySQL、Alembic 这些必须存在。3.2 创建数据库并导入演示数据fakeinfo.sql是个好东西它把初始科室、医生、药品、患者账号都准备好了不用自己一条条插入。前提是 MySQL 里先建一个库CREATE DATABASE hospital DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; USE hospital; SOURCE /完整路径/fakeinfo.sql;DEFAULT CHARACTER SET utf8mb4是库级编码设置COLLATE utf8mb4_unicode_ci是排序规则。导入完成后可以看一下核心表是否建立成功SHOW TABLES; SELECT COUNT(*) FROM user;如果表里能查到演示账号说明数据导入成功。需要注意fakeinfo.sql是带业务假数据的数据集里面可能包含身份证号、手机号、病历等模拟字段无论如何都不能把它直接当生产数据使用医院项目一旦涉及真实患者隐私数据脱敏不是可选项是红线。3.3 修改数据库连接并执行迁移我习惯把config.py里的数据库密码改成自己的本机密码而不是依赖默认的123456。在真实项目里数据库密码还应该走环境变量或密钥管理服务不能硬编码在源码里。改完后执行迁移python manage.py db upgrade如果没有迁移版本会显示已经是最新状态如果migrations/versions里有版本文件Alembic 会自动创建alembic_version表记录当前版本。另一个常见做法是让 SQLAlchemy 直接根据模型建表比如在 Python 交互环境里执行db.create_all()但这只适合快速原型不适合表结构要迭迭代的业务系统。既然项目里配了 Alembic就优先用迁移工具。参数说明db upgrade是向前迁移到最新版db downgrade是回退到上一个版本能回退多少取决于迁移文件里的down_revision链路。如果项目里有多个开发者在改表建议每次改model.py后就生成一次迁移并提交到代码库否则本地库里多了字段、别人库里还是旧结构联调时会翻车。3.4 启动服务并走通登录流程环境和数据都就位后启动就非常简单了python run.py看到类似这样的输出说明服务已起来* Running on http://127.0.0.1:5000/ (Press CTRLC to quit)然后浏览器打开http://127.0.0.1:5000/大概率会跳到登录页。演示账号可以从fakeinfo.sql里查也可以用 SQL 直接查SELECT id, username, role FROM user LIMIT 10;注意密码多半是 hash 之后存进库里的不要指望能直接看到明文。登录成功后会进入主界面左侧菜单通常能看到门诊、住院、划价、家庭医生等入口。这一步走通了说明整个工程的骨架是健康的如果卡在数据库连接或者登录跳转参考第 5 章逐条排查。4. 业务模块拆解门诊、住院、划价与家庭医生怎么协同4.1 auth 认证与 decorator.py登录状态是怎么被拦截的医院系统里几乎所有页面都需要登录后才能看如果每个视图函数都自己写一遍session.get(user_id)判断代码会非常啰嗦。工程里的decorator.py就是把这个逻辑抽象成了装饰器我推测核心代码类似# app/decorator.py from functools import wraps from flask import session, redirect, url_for def login_required(view): wraps(view) def wrapped(*args, **kwargs): # 医院系统里很多页面都要求登录后访问session 为空就回登录页 if not session.get(user_id): return redirect(url_for(auth.login)) return view(*args, **kwargs) return wrapped这种写法把“是否登录”的判断收敛到装饰器里业务视图只需要在函数定义上加一行login_required。走通流程后如果发现登录状态总失效先检查config.py里的SECRET_KEY这个密钥是用来给 session 签名防篡改的如果每次重启都随机生成浏览器里的旧 cookie 就全部失效表现就是“明明登录了一刷新又跳回登录页”。auth模块对应的蓝图负责真正的登录和登出逻辑流程一般是从表单拿用户名密码查用户表用check_password验证 hash验证通过后写入session[user_id]再跳转到首页。这里有一个新手常犯的问题登录成功以后只在 session 里存一个用户名不存用户 ID 和角色等出院模块要判断操作人身份时才发现缺数据。正确的做法是至少存user_id和role后续所有业务模块都能直接读。4.2 门诊与住院模块业务数据的读写路径门诊模块在真实场景下要解决的是“患者来医院从挂号到医生开单”的链路。工程里把这两个模块拆开意味着门诊看的是当次就诊住院看的是长期占用病床的记录。我在这类系统里总结出来的设计套路是门诊记录通常有一个visit或registration表包含患者 ID、医生 ID、科室 ID、就诊时间、诊断结论住院记录一般有admission表包含入院时间、出院时间、床号、主治医生两者都会关联患者主数据但业务状态完全独立。对应的 ORM 模型可以抽象成# app/model.py 片段仅示例关系 class Patient(db.Model): __tablename__ patient id db.Column(db.Integer, primary_keyTrue) name db.Column(db.String(64), nullableFalse) gender db.Column(db.String(8)) phone db.Column(db.String(20)) class OutpatientRecord(db.Model): __tablename__ outpatient_record id db.Column(db.Integer, primary_keyTrue) patient_id db.Column(db.Integer, db.ForeignKey(patient.id)) doctor_id db.Column(db.Integer, db.ForeignKey(user.id)) diagnosis db.Column(db.Text) created_at db.Column(db.DateTime)这里的关键在db.ForeignKey它让OutpatientRecord和Patient建立了关联。查询某位患者的历史门诊记录时可以visit_records OutpatientRecord.query.filter_by(patient_id1).all()写业务逻辑的时候要注意这种写法直接把 ORM 对象返回给了模板模板里可以便利地取关联字段但如果把对象转成 JSON 给前端就要留意序列化问题否则会报Object of type OutpatientRecord is not JSON serializable。这也是后面第 6 章改成 API 接口时最容易踩的坑。4.3 charges 与 familydoctor划价逻辑和随访记录怎么落地划价模块在传统医院流程里是“医生开处方后计算药品和检查费用”的环节。它和门诊模块的关系很紧密但不能直接写在门诊里因为费用计算涉及的金额、药品数量、医保类型和诊疗行为完全是另一套规则。职责分离之后门诊只管开单划价只管算钱。家庭医生模块则更偏向长期随访签约居民、定期回访、健康记录。它的特点是一张患者表可能对应多次随访记录所以访问频率和表设计都比门诊简单但状态字段很多比如签约状态、随访时间、下次随访时间。工程里把familydoctor独立出来正好符合“按业务生命周期拆模块”的思路。如果你接手后在app目录里看到很多__init__.py文件不用紧张那是 Python 包结构必需的没有它跨目录import就没法正常工作。4.4 数据安全与 MVC 边界医疗系统的红线不能碰摘要里反复提到数据安全和 MVC。MVC 的意思不是把文件命名成model.py就完事了而是要让模型层、视图层、控制层各干各的模型层负责表和业务规则视图层只负责渲染用户看到的东西控制层接收用户请求并调度。如果发现某个视图函数里直接写死了大段 SQL 拼接比如sql SELECT * FROM patient WHERE name %s % name这就是典型的反面教材。字符串拼接 SQL 很容易被注入正确的做法是用 SQLAlchemy 的 query 接口或参数化查询。医院系统的数据敏感程度高于普通网站密码必须用不可逆 hash 存储session cookie 必须配置好SECRET_KEY数据库账号权限也要按最小化原则分配演示工程里的root账号在生产环境绝对不能继续用。5. 避坑记录医院管理系统部署与运行的五个常见翻车点5.1 现象启动 run.py 报 ModuleNotFoundError提示缺少 Flask原因绝大多数情况是依赖没安装全或者 virtualenv 没有激活。也有人是全局环境 Python 版本太新导致某些旧依赖安装失败。解决先确认当前解释器位置执行which python如果路径不是项目里的venv/bin/python说明虚拟环境没激活。然后重装依赖建议用固定版本pip install -r requirements.txt如果某个包安装报编译错误优先把 Python 切回 3.8~3.11 这个区间很多老工程在 3.12 上会踩到 PyMySQL、cryptography 等依赖的兼容性问题。5.2 现象登录成功后刷新页面又跳回登录页像被“踢”出来原因这个坑我查过很多次八成是SECRET_KEY在每次应用启动时都变化。如果config.py里写的是SECRET_KEY os.urandom(24)每次重启进程密钥都会变旧 cookie 签名失效session 自然就丢了。解决改成固定的环境变量值本地开发可以直接写一个固定字符串SECRET_KEY os.environ.get(SECRET_KEY, hospital-dev-2024)部署时通过环境变量传入真实密钥代码库里不要保留生产密钥。浏览器端则清理一次 cookie重启服务后再登录验证。5.3 现象python manage.py db upgrade 执行时报迁移错误提示表已存在或字段重复原因最常见的情况是fakeinfo.sql已经把表结构和数据建好了再次执行db upgrade时 Alembic 想重新建表和已有表冲突。另一个情况是versions目录里有多个迁移文件某个版本的down_revision链断了。解决如果确认演示数据已经导入完整可以跳过upgrade或者用一个干净空库重新执行。推荐的做法是mysql -uroot -p -e DROP DATABASE hospital; CREATE DATABASE hospital CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; mysql -uroot -p hospital fakeinfo.sql然后在空库上执行python manage.py db upgrade让 Alembic 接管后续变更。迁移文件如果链断了可以查看alembic_version表和versions目录里的revision字段手动对齐当前基线。5.4 现象页面显示中文乱码姓名变成问号或者一串繁体乱码原因数据库连接串里没写charsetutf8mb4或者数据库本身是latin1编码中文存进去就是乱码。容器里如果 MySQL 初始化脚本没指定编码这个问题很容易出现。解决先改config.pySQLALCHEMY_DATABASE_URI ( mysqlpymysql://root:密码127.0.0.1:3306/hospital ?charsetutf8mb4 )同时确认库表编码ALTER DATABASE hospital CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; ALTER TABLE patient CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;如果只是新连接生效老数据还是乱码需要重新导入fakeinfo.sql。这个规则我在所有项目里都写进部署文档医院系统的地址、备注、医嘱都可能是中文编码问题能直接让演示过程翻车。5.5 现象静态图片或头像不显示刷新后 404原因Flask 默认把app/static作为静态目录但项目如果用的是蓝图蓝图下的static目录和全局static目录容易混淆。模板里写的路径如果带了蓝图名前缀比如/outpatient/static/xxx.png访问时就会 404。解决直接改成根路径img src{{ url_for(static, filenameimg/doctor.png) }} alt医生头像同时确认config.py里没有把STATIC_FOLDER指错地方。如果图片上传到本地磁盘而不是 static 目录需要额外配置上传文件的路由和访问权限不能只依赖url_for(static)。这个坑不算难但排查起来很费时间因为浏览器控制台和 Flask 日志都只显示 404不告诉你路径拼接错误。6. 把门诊视图改成 JSON 接口顺手补一条单元测试工程默认用 Jinja2 渲染网页但实际对接前端 App 或第三方系统时经常需要把视图返回 JSON。改造不需要伤筋动骨只需要在蓝图路由里区分请求类型。我一般会保留网页视图另外加一个 API 版本# app/outpatient/views.py from flask import jsonify, request from app import db from app.model import OutpatientRecord def list_records(): records OutpatientRecord.query.order_by(OutpatientRecord.id.desc()).all() data [] for r in records: data.append({ id: r.id, patient_id: r.patient_id, diagnosis: r.diagnosis, created_at: r.created_at.strftime(%Y-%m-%d %H:%M:%S) if r.created_at else None }) return jsonify({code: 0, data: data}) def create_record(): payload request.get_json() if not payload or not payload.get(patient_id): return jsonify({code: 400, msg: patient_id 必填}) record OutpatientRecord( patient_idpayload[patient_id], doctor_idpayload.get(doctor_id), diagnosispayload.get(diagnosis, ) ) db.session.add(record) db.session.commit() return jsonify({code: 0, data: {id: record.id}})这段代码的关键点是手动把created_at格式化成字符串因为 JSON 不能直接序列化datetime对象。如果直接jsonify(r.__dict__)会报类型错误这是 Flask 转 JSON 最常见的黑匣子之一。顺手写一条单元测试用 unittest 验证创建接口可以落库。测试数据库建议用独立的 SQLite 或事务回滚不污染开发库。这个工程的模块结构本身是解耦的所以测试代码也不用牵动整条业务链路。从那以后我每次拿到新工程都强制自己先跑通入口、把数据库迁到干净环境再补一条最小接口测试确认代码真的能“活”起来。希望帮到你。本文还有配套的精品资源点击获取

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑