简介基于Python开发的酒店管理系统数据库大作业适合计算机相关专业正在准备毕业设计、期末大作业或课程设计的学生可作为从数据库建模到系统界面实现的完整参考。资源共61个文件包含18个Python源文件、8个界面文件、3个数据库脚本以及2份设计文档压缩包约8.3MB目录按主程序、界面、数据库、文档等模块划分便于按需查阅。目前已有382人学习下载关键逻辑处附有注释简单配置本地环境后即可运行体验。项目附E-R图与系统功能结构图随包提供《数据库应用》系统设计报告和课程设计内容要求数据库脚本覆盖入住登记、房间管理等典型数据表便于理解业务功能与后台表结构的对应关系也能在此基础上改造扩展用于答辩或作业提交。1. 数据库大作业选 Python 酒店管理系统为什么这套方案最稳数据库大作业选酒店管理系统几乎是计算机专业里最不会翻车的组合它不像电商系统那样页面堆砌也不像进销存那样字段繁杂而是刚好把数据库设计里最常用的一套东西——表结构拆分、主外键、约束、事务、增删改查——全装进一个能跑起来的 Python 应用里。标题里的“源代码文档说明”不是摆设一份能过的课设要的不只是能跑的代码还要有数据库设计说明书、建表 SQL 和答辩口径。这套方案用 Python MySQL Tkinter 实现适合课程设计、毕业设计前期练手也适合想快速搭一个管理后台的人。照着从 ER 图推到建表再从 CRUD 推到界面最后交出去的是一个完整交付物。2. 数据库设计先行酒店管理系统的表结构要这样拆很多人拿到题目就先写界面写到订单表才发现房间、客户、订单三个实体之间的关系理不顺。做数据库大作业的正确顺序是先定 ER 模型和表结构再写 Python。酒店管理系统核心不是登录页而是订单表——它把房间和客户串起来所有业务流程都围着它转。下面先把三张核心表拆清楚。2.1 三张核心表怎么拆房间表、客户表、订单表房间表 room 记录酒店能卖的资源。字段要覆盖“这个房间什么样、什么价、现在什么状态”。room_id 用自增主键room_no 做业务编号必须唯一房型用字符串、价格用 DECIMAL(10,2)status 用 TINYINT 表示空闲、入住、脏房三种状态。别用 FLOAT 存钱用 DECIMAL。字段名类型约束说明room_idINTPK, AUTO_INCREMENT房间主键room_noVARCHAR(10)NOT NULL, UNIQUE房间号如 801room_typeVARCHAR(20)NOT NULL单人间/双人间/套房priceDECIMAL(10,2)NOT NULL门市价statusTINYINTNOT NULL, DEFAULT 00空闲 1入住 2脏房floorINT楼层remarkVARCHAR(255)备注客户表 customer 不要塞太多字段。姓名、电话、身份证号、注册时间就够。身份证号设 UNIQUE因为酒店场景里身份证是查询订单的常用凭证但要注意空值可以重复。字段名类型约束说明customer_idINTPK, AUTO_INCREMENT客户主键nameVARCHAR(50)NOT NULL客户姓名phoneVARCHAR(20)手机号id_cardVARCHAR(18)UNIQUE身份证号register_timeDATETIMEDEFAULT CURRENT_TIMESTAMP注册时间订单表 orders 是整个系统的主心骨。它同时引用 customer_id 和 room_id还要有入住日期、退房日期、状态。订单状态至少要有“预订、入住、已退房、已取消”四种后面做结账逻辑全靠它。字段名类型约束说明order_idINTPK, AUTO_INCREMENT订单主键customer_idINTNOT NULL, FK→customer下单客户room_idINTNOT NULL, FK→room入住的房间check_in_dateDATENOT NULL入住日期check_out_dateDATENOT NULL退房日期total_amountDECIMAL(10,2)DEFAULT 0实收金额statusTINYINTNOT NULL, DEFAULT 00预订 1入住 2已退房 3已取消create_timeDATETIMEDEFAULT CURRENT_TIMESTAMP下单时间一个客户可以下多张订单一张订单只能挂在一个客户下所以 customer 和 orders 是一对多room 和 orders 也是一对多同一房间可以反复被预订。中间不需要再多加关联表。2.2 从 ER 图到建表 SQL主键、外键与约束在设计图上room 在左边、customer 在右边orders 在中间两端分别连一条 1—N 的线这就是交付文档里需要的 ER 图。答辩时老师会看三处主键是否清晰、外键是否正确、约束和数据一致性有没有考虑。建库建表 SQL 是整套系统的地基。命名上注意 order 是 SQL 关键字表名一律用 orders 复数避免麻烦。字符集统一用 utf8mb4否则中文会乱。引擎用 InnoDB因为它支持事务和外键MyISAM 在这个题目里没有优势。CREATE DATABASE IF NOT EXISTS hotel_db DEFAULT CHARACTER SET utf8mb4; USE hotel_db; CREATE TABLE room ( room_id INT AUTO_INCREMENT PRIMARY KEY, room_no VARCHAR(10) NOT NULL UNIQUE, room_type VARCHAR(20) NOT NULL, price DECIMAL(10,2) NOT NULL, status TINYINT NOT NULL DEFAULT 0 COMMENT 0-空闲 1-入住 2-脏房, floor INT, remark VARCHAR(255) ) ENGINEInnoDB; CREATE TABLE customer ( customer_id INT AUTO_INCREMENT PRIMARY KEY, name VARCHAR(50) NOT NULL, phone VARCHAR(20), id_card VARCHAR(18) UNIQUE, register_time DATETIME DEFAULT CURRENT_TIMESTAMP ) ENGINEInnoDB; CREATE TABLE orders ( order_id INT AUTO_INCREMENT PRIMARY KEY, customer_id INT NOT NULL, room_id INT NOT NULL, check_in_date DATE NOT NULL, check_out_date DATE NOT NULL, total_amount DECIMAL(10,2) DEFAULT 0, status TINYINT NOT NULL DEFAULT 0 COMMENT 0-预订 1-入住 2-已退房 3-已取消, create_time DATETIME DEFAULT CURRENT_TIMESTAMP, CONSTRAINT fk_order_customer FOREIGN KEY (customer_id) REFERENCES customer(customer_id), CONSTRAINT fk_order_room FOREIGN KEY (room_id) REFERENCES room(room_id) ) ENGINEInnoDB;这段 DDL 说明三点。第一status 用 TINYINT 而不是字符串后续 Python 拿到的是 0/1/2比中文状态更容易做条件判断如果一定要显示文字界面层自己映射。第二total_amount 允许默认 0退房前不一定要算好价格预订时金额为空是常态。第三外键默认的约束是 RESTRICT也就是说 customer 或者 room 里有订单引用时不能直接删掉那一行这能防止你手滑删出脏数据。如果表已经建好又发现字段不够用 ALTER TABLE 补。大作业里最常改的是 orders 表加备注字段ALTER TABLE orders ADD COLUMN remark VARCHAR(255) DEFAULT NULL;改完记得在文档数据字典里同步尤其注意字段顺序。一遍遍改表很繁琐设计时多花十分钟想清楚状态流转后面能少改一大半。2.3 为什么选 MySQL 而不是 SQLite我见过不少同学图省事用 SQLite因为它是文件型数据库、零配置。可这是数据库大作业答辩老师默认你会装 MySQL 并用可视化工具建表。MySQL 的生态更接近生产环境导入导出、权限管理、事务这些都讲得清楚SQLite 在课设里容易被追问“这个项目离了本地文件还能不能独立运行”一句答不上来印象分会掉。而且你还要做数据库增删改查的演示用 Navicat 或 DBeaver 连 MySQL 点表、看 ER 图比命令行里敲 SQLite 直观得多。这里也要提一个选型上的思路差异。酒店管理系统是“订单驱动”的系统最核心的流程是订房、入住、退房、结账客户信息只是订单的附属维度。这和侧重客户关系维护、营销生命周期的酒店客户关系管理系统CRM完全不同CRM 会把客户表设计成主维度围绕客户标签、消费偏好、会员等级去建模。如果你的大作业题目同时给了“酒店管理系统和客户关系管理系统的区别”这类问题答辩时就说清楚这套系统以房态和订单为主客户表做薄才符合业务重心。MySQL 还有一个好处是可以做数据库连接池、主从同步这些扩展。课设不一定用得上但你把连接封装成模块后留一个口子说明“生产环境会改成连接池”这种表述本身就是加分项。3. Python 连接 MySQL用 pymysql 封装一个 DBHelper表建好以后进入 Python 侧。连接数据库是这个环节的第一个卡点。选用 pymysql它是纯 Python 实现的 MySQL 客户端安装简单不用编译兼容 Python 3 的常用版本。唯一要注意的是 MySQL 版本和 pymysql 的匹配问题MySQL 8 以上默认认证插件是 caching_sha2_password如果连接报错在 MySQL 里把 root 用户的插件改回 mysql_native_password或者升级 pymysql。3.1 安装 pymysql连接参数逐项说明先安装依赖pip install pymysql连接参数在代码里长这样import pymysql conn pymysql.connect( host127.0.0.1, port3306, userroot, password123456, databasehotel_db, charsetutf8mb4, autocommitFalse )host 和 port 一般填 127.0.0.1 和 3306如果你 MySQL 装在另一台机器上或者用了容器这里填对应 IP 和映射端口。charset 必须和建库字符集保持一致utf8mb4 才能存全量 Unicode 字符。autocommitFalse 是故意关掉的数据库大作业要求演示事务关闭自动提交后insert、update 必须 commit 才真正落库这正好让你在代码里把事务逻辑写出来。MySQL 8 默认的认证插件是个高频坑。如果你报的是Authentication plugin caching_sha2_password cannot be loaded解决方法是执行一句话ALTER USER rootlocalhost IDENTIFIED WITH mysql_native_password BY 123456;注意这个 SQL 只改 root 本地登录的方式不影响数据内容。3.2 封装 DBHelper连接不散落在业务代码里最简单粗暴的写法是每个函数里都写一遍 pymysql.connect然后 cursor、execute、close。代码一多就散得到处都是出问题时每个函数都要改。我一般会先做一个 DBHelper 类把所有连接、查询、提交、回滚收口。import pymysql class DBHelper: def __init__(self, **kwargs): self.config { host: kwargs.get(host, 127.0.0.1), port: kwargs.get(port, 3306), user: kwargs.get(user, root), password: kwargs.get(password, ), database: kwargs.get(database, hotel_db), charset: utf8mb4, autocommit: False, } def _connect(self): return pymysql.connect(**self.config) def query(self, sql, paramsNone): conn self._connect() try: with conn.cursor() as cursor: cursor.execute(sql, params) return cursor.fetchall() finally: conn.close() def execute(self, sql, paramsNone): conn self._connect() try: with conn.cursor() as cursor: cursor.execute(sql, params) conn.commit() return cursor.rowcount except Exception: conn.rollback() raise finally: conn.close()query 用于只读操作返回元组列表execute 用于 insert、update、delete返回受影响行数并且自动 commit。commit 放在 with 块外面是因为 cursor 和事务没有强绑定先确认 execute 没抛异常再提交逻辑更清楚。finally 里一定 close否则连接句柄会越攒越多跑一晚上内存就上去了。这里还可以预留一个连接池的口子数据量大时把 _connect 替换成 DBUtils.PooledDB 的实例即可其余代码不用动。这种“对外接口不变、内部换实现”的做法放到数据库设计说明里可以作为亮点。3.3 参数化查询与事务增删改查的安全写法数据库增删改查是课设文档里必须单列出来的一节。最容易拿分的是用参数化查询而不是拼接字符串。下面这段是典型的“拼接翻车”写法# 错误示范 sql SELECT * FROM customer WHERE name name 如果 name 传入 OR 11 --整张表都会被查出来。答辩老师常问“怎么防 SQL 注入”你把参数化查询写进代码再解释两句这题就过了。正确写法是把 %s 占位符和参数分开传db DBHelper(password123456) # 查询空闲房间 rooms db.query( SELECT room_id, room_no, room_type, price FROM room WHERE status %s, (0,) ) # 新增客户 affected db.execute( INSERT INTO customer(name, phone, id_card) VALUES (%s, %s, %s), (张三, 13800001111, 110101199001011234) )注意这里的 %s 是 pymysql 的参数占位符不是 Python 字符串格式化里的 %。pymysql 在拿到 SQL 和参数后会把值安全转义避免值里带引号、百分号破坏语法。多表关联查询也要会写。入住报表、订单列表这类功能用 join 就能一次取到多张表的数据SELECT o.order_id, r.room_no, c.name, o.check_in_date, o.check_out_date, o.total_amount FROM orders o JOIN room r ON o.room_id r.room_id JOIN customer c ON o.customer_id c.customer_id WHERE o.status 1把这段 SQL 作为字符串丢进 query 方法就行。课设里尽量少用子查询join 的可读性和效率都更稳。事务是另一个答辩必考点。比如入住登记时要先写 customer、再写 orders、再把 room 改成已入住三步必须同时成功。单个 execute 方法每次都 commit无法保证三个操作的一致性。我通常会给 DBHelper 再补一个事务方法def transaction(self, func): conn self._connect() try: with conn.cursor() as cursor: result func(cursor) conn.commit() return result except Exception: conn.rollback() raise finally: conn.close()调用时把业务写成回调函数cursor 在外部传入里面可以连续执行多条 SQLdef check_in(cursor): cursor.execute( INSERT INTO customer(name, phone, id_card) VALUES (%s, %s, %s), (李四, 13900002222, 110101199501011234) ) customer_id cursor.lastrowid cursor.execute( INSERT INTO orders(customer_id, room_id, check_in_date, check_out_date, status) VALUES (%s, %s, %s, %s, 1), (customer_id, 101, 2025-05-20, 2025-05-22) ) cursor.execute( UPDATE room SET status 1 WHERE room_id %s, (101,) ) db.transaction(check_in)cursor.lastrowid 拿到自增主键这是处理“先插入主表再插入从表”的标准做法。回调里任意一步抛异常rollback 会把之前写的东西全部还原。这就是事务的意义。数据库大作业里如果能把这段讲清楚比贴在代码注释里管用得多。4. 用 Tkinter 把界面搭起来前台下单、后台管理Python 生态里做课设界面有 PyQt、Flask 网页和 Tkinter 三条路。PyQt 功能强但文件多Flask 需要浏览器和前端模板Tkinter 是标准库自带、打包简单单文件能直接跑所以这套方案选 Tkinter。界面不需要华丽把房态、下单、退房三条主线做成窗口和表格就行。4.1 登录角色与主界面布局先建一张员工表做登录区分前台和管理员。这属于系统的基础数据必须放在数据库设计文档里。CREATE TABLE employee ( emp_id INT AUTO_INCREMENT PRIMARY KEY, username VARCHAR(30) NOT NULL UNIQUE, password VARCHAR(64) NOT NULL, role TINYINT NOT NULL DEFAULT 0 COMMENT 0-前台 1-管理员 ) ENGINEInnoDB;密码字段建议至少 64 位因为可以用 sha256 或加盐哈希存课设里不要求特别安全但字段宽度要预留。登录窗口拿到用户名密码后查这张表role 字段决定主界面是否显示“报表”和“员工管理”按钮。前台只有开单、退房、查房态三个操作管理员多一个营业报表入口。这样设计的好处是答辩时有得讲——权限控制不是写在界面层而是数据字段在起作用。主界面布局我一般用左右分栏左边是 Treeview 房态表右侧放操作表单。窗口标题直接叫“酒店管理系统”底部放一个“刷新房态”按钮。4.2 用 Treeview 展示房态并刷新Treeview 是 Tkinter 里最接近表格的控件。展示房态的最小代码如下import tkinter as tk from tkinter import ttk db DBHelper(password123456) root tk.Tk() root.title(酒店管理系统) columns (room_no, room_type, price, status) tree ttk.Treeview(root, columnscolumns, showheadings) for col in columns: tree.heading(col, textcol) tree.pack(filltk.BOTH, expandTrue) def refresh_rooms(): for row in tree.get_children(): tree.delete(row) rows db.query(SELECT room_no, room_type, price, status FROM room) for row in rows: tree.insert(, end, valuesrow) refresh_rooms() root.mainloop()这里有一个非常容易翻车的点Treeview 刷新前必须先删除旧行否则每次刷新都在原有数据上叠加。清空用tree.get_children()拿到所有子项 ID再逐个 delete。status 显示成 0 和 1 可读性差建议在 SQL 里用 CASE WHEN 转为中文SELECT room_no, room_type, price, CASE status WHEN 0 THEN 空闲 WHEN 1 THEN 入住 ELSE 脏房 END AS status FROM room这段 SQL 同时教你一个实用操作状态转换交给数据库界面只负责展示。比在 Python 里写 if else 更直观。4.3 预订、入住、退房的业务流程与金额计算预订和入住的差别其实只是订单状态不同。预订流程选房间、填客户、写入订单status0入住流程在预订基础上把房间状态改成已入住。这里代码可以共用一套数据录入界面。关键在退房结账。金额计算要考虑两件事住几天、按门市价还是折扣价。最简单的是用 DATEDIFF 算天数再乘房间单价def check_out(order_id, room_id, out_date): rows db.query( SELECT DATEDIFF(%s, check_in_date) FROM orders WHERE order_id %s, (out_date, order_id) ) days rows[0][0] if days 0: print(退房日期不能早于入住日期) return price db.query( SELECT price FROM room WHERE room_id %s, (room_id,) )[0][0] amount days * price def op(cursor): cursor.execute( UPDATE orders SET status 2, total_amount %s WHERE order_id %s, (amount, order_id) ) cursor.execute( UPDATE room SET status 0 WHERE room_id %s, (room_id,) ) db.transaction(op)退房是典型的“两表联动”事务先更新订单状态和金额再释放房间。如果分两次 commit订单改了房间没改房态就错了。所以这里必须走 3.3 节的 transaction。答辩时拿这个例子讲“为什么需要事务”非常具体。金额计算这个函数放在一个单独的 service 模块里不要塞进按钮回调方便写单元测试。大作业不要求测试覆盖率但至少你要能在文档里写清楚“输入退房日期输出应收金额”。界面层还有一个小技巧把 Treeview 当前选中的行绑定到变量上。选中房间后预订按钮才可用否则弹提示。这算交互细节但能给演示加分。5. Python 酒店管理系统避坑手册五个最容易让大作业翻车的问题代码能跑起来是一回事跑起来不出错是另一回事。下面五件事是我在帮别人查课设时遇到频率最高的按现象、原因、解决的顺序列出来。查问题的时候先按这个表过一遍。5.1 连接与字符集Access denied 和中文乱码现象一连接 MySQL 时报Access denied for user rootlocalhost。原因多半是密码不对或者 root 默认只允许 localhost 登录。打开 MySQL 命令行用你自己的密码试一下如果是在另一台机器上连检查用户表的 host 字段是不是%并执行GRANT ALL PRIVILEGES ON hotel_db.* TO root%;。改完记得 FLUSH PRIVILEGES。现象二插入中文数据后表里显示乱码或者直接报Incorrect string value。原因是建库、建表、连接三个环节的字符集不统一。最省事的做法是从一开始建库就写DEFAULT CHARACTER SET utf8mb4连接参数里也带上charsetutf8mb4两处保持一致。老库已经建错了用 ALTER DATABASE / ALTER TABLE 改 charset但要注意已有数据的转码。ALTER DATABASE hotel_db CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; ALTER TABLE customer CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;5.2 数据写入与界面刷新外键失败和 Treeview 不更新现象三插入订单时抛Cannot add or update a child row: a foreign key constraint fails。原因是 orders 表里的 customer_id 或 room_id 在对应表里根本不存在。最常见的情况是房间号是手输的而不是从 room 表选的或者测试数据里删过房间但订单还在。解决业务逻辑里强制房态选择先用 SQL 查出合法房间再写订单如果只是想快速清掉坏数据把外键临时关掉再调整数据SET FOREIGN_KEY_CHECKS 0; UPDATE orders SET room_id 101 WHERE order_id 1; SET FOREIGN_KEY_CHECKS 1;这个开关只能用来修数据不要写进正式流程。正式流程仍然要依赖外键保证一致性。现象四界面点了预订数据库里订单已经插入但 Treeview 房态没有变化。原因写在 4.2刷新前没有清空旧行。tree.delete(*tree.get_children())这个写法一次清空比你用 for 循环逐个删更干净且更快。刷新时机也要注意预订成功后立刻调用 refresh_rooms()而不是等用户手动点刷新如果用了多窗口数据库变了但旧窗口还持有旧数据记得在打开窗口时重新 query。5.3 打包与环境exe 连不上 MySQL 和配置文件写死现象五代码在本机能跑用 PyInstaller 打包成 exe 后到别的机器上双击程序报连不上数据库。原因很直白代码里把连接参数写死了换成没有 MySQL 的机器自然连不上或者数据库在别人机器上Host 还是 127.0.0.1。解决方式是做一个配置文件把数据库连接参数抽出来[mysql] host 192.168.1.10 port 3306 user root password 123456 database hotel_dbPython 侧用 configparser 读取import configparser config configparser.ConfigParser() config.read(config.ini) db DBHelper( hostconfig[mysql][host], portconfig[mysql].getint(port), userconfig[mysql][user], passwordconfig[mysql][password], databaseconfig[mysql][database] )打包时把 config.ini 一并放进 dist 目录部署在装有 MySQL 的环境上就能改配置直接连。如果你想让演示机器也免装 MySQL可以把数据迁到局域网内的 MySQL 上然后把 host 改成服务器 IP——这就是“数据库独立于客户端”的一种落地。答辩时可以明确说客户端不持有存储层更符合生产结构。提示PyInstaller 打包 Tkinter 程序时会带上必要的动态库但 config.ini 不会自动包含需要用--add-data指定。如果没有特殊要求我建议把 config.ini 放在 exe 同目录代码里用os.path.dirname(sys.executable)拼路径而不是依赖默认当前目录。以上五条覆盖了从开发到交付全过程最常见的故障点。每一条都在文档里配上截图和操作步骤你的“运行说明”就直接能写成一份排错手册。6. 文档说明和答辩准备数据库设计说明书写得细一点分就高一点标题里既然带了“文档说明”它就不只是附赠材料而是分数构成的一部分。我建议按这个顺序写需求分析 → ER 图 → 数据字典 → 建表 SQL → 功能实现 → 系统测试 → 运行说明。ER 图可以用 Navicat 或 DBeaver 直接逆向生成截图放进文档数据字典把第 2 章表格整理成 Word 表格标注每个字段的类型、长度、约束和含义。源代码在交的时候不要只丢一个 .py按 dbhelper.py、service.py、views.py、main.py 四个模块拆开文档里对应画一张模块调用关系图。答辩的时候把精力放在四个问题上。第一表结构为什么要这么拆——答订单驱动订单表做中间桥梁。第二外键和事务有什么用——举退房结账的例子。第三怎么防 SQL 注入——指向参数化查询。第四如果订单金额需要按会员打折你的表结构加什么字段——这是开放题答在 customer 表加 member_level在 orders 表加 discount_rate就够用了。如果老师问“酒店管理系统和客户关系管理系统的区别”集中讲清楚客户表在这里只是订单附属系统重心是房态与账目。我自己的习惯是交作业前把代码从头跑一遍按文档里的菜单路径一步一步截图截图命名成“4-1登录窗口”“4-2房态列表”这种格式直接嵌进 Word 里。这一步翻车的人很多——文档里写的功能代码里没有答辩演示到一半才发现窗口打不开。做完这些剩下的就是答辩时把关键 SQL 和业务函数放在手边老师问到哪你点到哪。希望帮到你。这套方向做完不只是交掉一门大作业你等于把数据库建模、CRUD、事务、桌面 GUI 这一条链路完整走了一遍后面找工作笔试里的 SQL 题大概率也逃不出这些内容。本文还有配套的精品资源点击获取
阅读完成 · 觉得有帮助?