Python+Flask+MySQL学生宿舍管理系统毕业设计实战:从环境搭建到部署答辩

发布时间:2026/10/7 17:22:07
Python+Flask+MySQL学生宿舍管理系统毕业设计实战:从环境搭建到部署答辩 简介本资源为基于PythonFlaskMySQL的学生宿舍管理系统完整源码与数据库面向计算机相关专业学生及需要完成毕业设计、期末大作业或课程设计的开发者。项目难度适中源码经本地编译验证可运行评审得分达98分内容经助教老师审定能帮助读者快速搭建一套具备宿舍信息管理、学生入住登记等典型业务模块的Web系统。压缩包共1955个文件以889个py源码与889个pyc编译文件为核心另含txt说明、exe可执行程序、wheel依赖包、js与vue前端资源、sql数据库脚本及png、svg等界面素材整体约13.07MB目录结构完整便于按模块查阅与二次开发。目前已有236人学习下载。读者可从中获得可直接运行的工程代码、配套数据库文件与依赖清单用于理解Flask项目分层组织、前后端交互流程及MySQL表结构设计也可作为答辩演示与功能扩展的基础模板。1. 学生宿舍管理系统从课程作业到毕业设计的最后一公里每年毕业季计算机相关专业的学生都会面对同一个问题毕业设计做什么。教务系统、图书管理、超市收银这些题目已经被做烂了查重率居高不下答辩时老师看一眼就摇头。学生宿舍管理系统是一个相对冷门但实用性很强的选题它涉及用户权限、数据增删改查、多表关联、状态流转这些核心知识点工作量适中功能边界清晰非常适合作为本科毕业设计。这个系统本质上是一个典型的信息管理系统用 Python 做后端逻辑Flask 提供 Web 框架支撑MySQL 负责数据持久化。它要解决的核心问题是宿管员如何高效管理几百上千名学生的住宿信息包括入住、调宿、退宿、报修、访客登记这些日常事务。适合谁做适合已经学过 Python 基础、了解 HTML 和 SQL 基本语法、需要在一个月内完成一个能跑起来、能演示、能写论文的毕业设计的同学。下面从环境搭建到功能实现把这条路走通。2. 环境搭建Python、Flask、MySQL 三件套的安装与配置2.1 Python 安装与虚拟环境创建Python 安装是第一步也是最容易翻车的地方。Windows 10 上建议直接去 python.org 下载 3.8 或 3.9 版本的安装包不要用 Microsoft Store 里的版本那个版本路径权限有问题后面装库会报一堆莫名其妙的错。安装时务必勾选“Add Python to PATH”否则命令行里敲 python 会提示找不到命令。安装完成后验证python --version pip --version如果两条命令都能正常输出版本号说明安装成功。接下来创建虚拟环境这是好习惯避免不同项目的依赖互相污染# 在项目根目录下执行 python -m venv venv # Windows 激活虚拟环境 venv\Scripts\activate # 激活后命令行前面会出现 (venv) 标识虚拟环境激活后安装 Flask 和 MySQL 驱动pip install flask pip install flask-sqlalchemy pip install pymysql pip install flask-login这里解释一下这几个库的分工。Flask 是 Web 框架本体负责路由和请求处理。Flask-SQLAlchemy 是 ORM 层让你用 Python 类的方式操作数据库表不用手写大量 SQL。PyMySQL 是 MySQL 的 Python 驱动SQLAlchemy 底层通过它连接数据库。Flask-Login 负责用户会话管理处理登录状态和权限校验。版本方面Flask 2.x 和 3.x 都可以但注意 Flask 3.x 对 Werkzeug 版本有要求如果遇到兼容性报错锁定 Flask2.3.3 和 Werkzeug2.3.7 这两个版本最稳妥。提示如果 pip 安装速度慢可以临时指定国内镜像源加上-i https://pypi.tuna.tsinghua.edu.cn/simple参数。2.2 MySQL 安装与数据库初始化MySQL 在 Windows 10 上的安装有两种主流方式一是下载 MSI 安装包图形化安装二是下载 ZIP 压缩包手动配置。新手建议用 MSI 安装包一路下一步就行但有几个关键点要注意。安装类型选择“Custom”然后手动勾选 MySQL Server 和 MySQL Workbench。Workbench 是图形化管理工具后面建表、查数据都靠它必装。认证方式选择“Use Strong Password Encryption”设置 root 密码时记牢建议用简单一点的密码比如root123456本地开发不用太复杂。Windows Service 那一步勾选“Configure MySQL Server as a Windows Service”服务名默认 MySQL80 即可这样开机自动启动不用每次手动开。安装完成后验证mysql -u root -p # 输入密码后进入 MySQL 命令行如果提示mysql不是内部或外部命令说明 MySQL 的 bin 目录没有加到系统 PATH 里。找到 MySQL 安装目录下的 bin 文件夹比如C:\Program Files\MySQL\MySQL Server 8.0\bin把这个路径加到系统环境变量 Path 中重启命令行即可。接下来创建数据库和专用用户-- 创建数据库字符集用 utf8mb4 支持中文和特殊符号 CREATE DATABASE dormitory_db CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; -- 创建专用用户避免直接用 root 连接 CREATE USER dorm_userlocalhost IDENTIFIED BY dorm_pass_2024; -- 授权 GRANT ALL PRIVILEGES ON dormitory_db.* TO dorm_userlocalhost; FLUSH PRIVILEGES;字符集必须用 utf8mb4不要用 utf8。MySQL 里的 utf8 是残缺的只支持三个字节存不了 emoji 和部分生僻字学生姓名里如果有生僻字就会乱码。utf8mb4 才是真正的四字节 UTF-8。2.3 Flask 项目骨架与数据库连接配置项目目录结构建议这样组织dormitory_system/ ├── app.py # 应用入口 ├── config.py # 配置文件 ├── models.py # 数据模型 ├── routes/ │ ├── auth.py # 登录注册路由 │ ├── student.py # 学生管理路由 │ └── dorm.py # 宿舍管理路由 ├── templates/ # Jinja2 模板 ├── static/ # 静态文件 └── requirements.txt # 依赖清单config.py里写数据库连接串# config.py import os class Config: SECRET_KEY os.environ.get(SECRET_KEY) or your-secret-key-here SQLALCHEMY_DATABASE_URI mysqlpymysql://dorm_user:dorm_pass_2024localhost:3306/dormitory_db?charsetutf8mb4 SQLALCHEMY_TRACK_MODIFICATIONS False SQLALCHEMY_ENGINE_OPTIONS { pool_size: 10, pool_recycle: 3600, pool_pre_ping: True }连接串格式是mysqlpymysql://用户名:密码主机:端口/数据库名?charsetutf8mb4。pool_pre_ping这个参数很关键它让 SQLAlchemy 在每次从连接池取连接时先 ping 一下如果连接已经断了就自动重连。MySQL 默认 8 小时不活动会断开连接没有这个参数的话第二天早上来跑项目就会报Lost connection to MySQL server的错误这是血泪经验。app.py初始化from flask import Flask from flask_sqlalchemy import SQLAlchemy from flask_login import LoginManager from config import Config db SQLAlchemy() login_manager LoginManager() def create_app(): app Flask(__name__) app.config.from_object(Config) db.init_app(app) login_manager.init_app(app) login_manager.login_view auth.login from routes.auth import auth_bp from routes.student import student_bp from routes.dorm import dorm_bp app.register_blueprint(auth_bp) app.register_blueprint(student_bp) app.register_blueprint(dorm_bp) return app if __name__ __main__: app create_app() app.run(debugTrue, host0.0.0.0, port5000)这里用了应用工厂模式create_app()函数负责创建和配置应用实例。蓝图Blueprint用来拆分路由不同功能模块放在不同文件里避免所有路由堆在一个文件里。login_manager.login_view指定未登录用户访问受保护页面时跳转到哪个路由。3. 数据模型设计五张核心表撑起整个系统3.1 用户表与角色权限设计学生宿舍管理系统的用户分三种角色管理员、宿管员、学生。管理员拥有全部权限宿管员只能管理自己楼栋的数据学生只能查看自己的住宿信息和提交报修。角色权限用一张用户表加一个角色字段来实现# models.py from app import db from flask_login import UserMixin from werkzeug.security import generate_password_hash, check_password_hash from datetime import datetime class User(UserMixin, db.Model): __tablename__ users id db.Column(db.Integer, primary_keyTrue, autoincrementTrue) username db.Column(db.String(50), uniqueTrue, nullableFalse, indexTrue) password_hash db.Column(db.String(255), nullableFalse) real_name db.Column(db.String(50), nullableFalse) role db.Column(db.Enum(admin, manager, student), defaultstudent, nullableFalse) phone db.Column(db.String(20)) created_at db.Column(db.DateTime, defaultdatetime.utcnow) # 关联学生住宿记录 dorm_records db.relationship(DormRecord, backrefstudent, lazydynamic) def set_password(self, password): self.password_hash generate_password_hash(password) def check_password(self, password): return check_password_hash(self.password_hash, password) def is_admin(self): return self.role admin def __repr__(self): return fUser {self.username}密码绝对不能明文存储generate_password_hash默认用 pbkdf2:sha256 算法加盐哈希同一个密码每次生成的哈希值都不一样无法反推。check_password用来校验登录密码。role字段用 Enum 类型限定只能取三个值数据库层面就做了约束避免脏数据。indexTrue在 username 上建了索引登录时按用户名查询会走索引速度快很多。学生表数据量大了之后没有索引的查询会明显变慢。3.2 宿舍楼、房间、住宿记录三表关联宿舍管理的核心数据关系是一栋楼有多个房间一个房间有多个床位一个床位可以住一个学生。用三张表来表达class Building(db.Model): __tablename__ buildings id db.Column(db.Integer, primary_keyTrue) name db.Column(db.String(50), uniqueTrue, nullableFalse) floors db.Column(db.Integer, nullableFalse, default6) gender db.Column(db.Enum(male, female), nullableFalse) manager_id db.Column(db.Integer, db.ForeignKey(users.id)) rooms db.relationship(Room, backrefbuilding, lazydynamic) class Room(db.Model): __tablename__ rooms id db.Column(db.Integer, primary_keyTrue) building_id db.Column(db.Integer, db.ForeignKey(buildings.id), nullableFalse) room_number db.Column(db.String(10), nullableFalse) capacity db.Column(db.Integer, default4) current_count db.Column(db.Integer, default0) __table_args__ ( db.UniqueConstraint(building_id, room_number, nameuq_building_room), ) records db.relationship(DormRecord, backrefroom, lazydynamic) class DormRecord(db.Model): __tablename__ dorm_records id db.Column(db.Integer, primary_keyTrue) student_id db.Column(db.Integer, db.ForeignKey(users.id), nullableFalse) room_id db.Column(db.Integer, db.ForeignKey(rooms.id), nullableFalse) bed_number db.Column(db.Integer, nullableFalse) check_in_date db.Column(db.Date, nullableFalse) check_out_date db.Column(db.Date) status db.Column(db.Enum(active, checked_out), defaultactive) __table_args__ ( db.UniqueConstraint(room_id, bed_number, status, nameuq_room_bed_active), )UniqueConstraint这里有两个关键约束。第一个在 Room 表上保证同一栋楼里房间号不重复。第二个在 DormRecord 表上保证同一个房间同一个床位在 active 状态下只能有一条记录防止一个床位同时分配给两个人。这个约束在调宿场景下特别重要没有它的话代码逻辑稍微写错就会出现数据不一致。current_count字段是冗余字段记录当前房间已住人数。每次分配或退宿时同步更新。这样做的好处是查询空床位时不用去 count DormRecord 表直接读 Room 表的 current_count 就行性能好很多。代价是需要在业务逻辑里保证一致性后面会讲怎么处理。3.3 报修表与访客登记表报修和访客是系统的辅助功能但也是论文里体现工作量的部分class RepairRequest(db.Model): __tablename__ repair_requests id db.Column(db.Integer, primary_keyTrue) student_id db.Column(db.Integer, db.ForeignKey(users.id), nullableFalse) room_id db.Column(db.Integer, db.ForeignKey(rooms.id), nullableFalse) content db.Column(db.Text, nullableFalse) status db.Column(db.Enum(pending, processing, done), defaultpending) created_at db.Column(db.DateTime, defaultdatetime.utcnow) resolved_at db.Column(db.DateTime) class VisitorLog(db.Model): __tablename__ visitor_logs id db.Column(db.Integer, primary_keyTrue) visitor_name db.Column(db.String(50), nullableFalse) visitor_phone db.Column(db.String(20)) student_id db.Column(db.Integer, db.ForeignKey(users.id), nullableFalse) visit_time db.Column(db.DateTime, defaultdatetime.utcnow) leave_time db.Column(db.DateTime) reason db.Column(db.String(200))报修表的状态流转是 pending → processing → done宿管员接单后改成 processing处理完改成 done 并记录 resolved_at 时间。访客表记录来访和离开时间方便查询某段时间内某栋楼的访客情况。建表用 Flask-Migrate 或者直接db.create_all()。开发阶段用db.create_all()就够了在app.py里加一行with app.app_context(): db.create_all()运行一次后五张表就建好了。生产环境建议用 Flask-Migrate 做版本管理但毕业设计用create_all完全够用。4. 核心功能实现从登录到退宿的完整链路4.1 登录认证与角色路由拦截登录功能用 Flask-Login 实现核心是login_user和logout_user两个函数以及login_required装饰器# routes/auth.py from flask import Blueprint, render_template, redirect, url_for, flash, request from flask_login import login_user, logout_user, login_required, current_user from models import User from app import db auth_bp Blueprint(auth, __name__) auth_bp.route(/login, methods[GET, POST]) def login(): if request.method POST: username request.form.get(username, ).strip() password request.form.get(password, ) if not username or not password: flash(用户名和密码不能为空) return render_template(login.html) user User.query.filter_by(usernameusername).first() if user is None or not user.check_password(password): flash(用户名或密码错误) return render_template(login.html) login_user(user, rememberTrue) # 根据角色跳转到不同首页 if user.role admin: return redirect(url_for(student.dashboard)) elif user.role manager: return redirect(url_for(dorm.manager_dashboard)) else: return redirect(url_for(student.my_dorm)) return render_template(login.html) auth_bp.route(/logout) login_required def logout(): logout_user() return redirect(url_for(auth.login))login_user(user, rememberTrue)里的rememberTrue会往用户浏览器写一个长期 cookie关掉浏览器再打开还是登录状态。毕业设计演示时这个功能很实用不用每次重启服务都重新登录。角色拦截用装饰器实现from functools import wraps from flask import abort from flask_login import current_user def role_required(*roles): def decorator(f): wraps(f) def decorated_function(*args, **kwargs): if not current_user.is_authenticated: return redirect(url_for(auth.login)) if current_user.role not in roles: abort(403) return f(*args, **kwargs) return decorated_function return decorator # 使用示例 student_bp.route(/admin/students) login_required role_required(admin, manager) def student_list(): # 只有管理员和宿管员能访问 passrole_required接收可变参数可以传多个角色。abort(403)返回 403 禁止访问页面比跳转回登录页更合理因为用户已经登录了只是权限不够。4.2 宿舍分配与调宿的事务处理宿舍分配是系统里最复杂的业务逻辑涉及多张表的写操作必须用事务保证一致性# routes/dorm.py from flask import Blueprint, request, jsonify from flask_login import login_required, current_user from app import db from models import Room, DormRecord, User from datetime import date dorm_bp Blueprint(dorm, __name__) dorm_bp.route(/api/assign, methods[POST]) login_required role_required(admin, manager) def assign_dorm(): data request.get_json() student_id data.get(student_id) room_id data.get(room_id) bed_number data.get(bed_number) # 参数校验 if not all([student_id, room_id, bed_number]): return jsonify({code: 400, msg: 参数不完整}), 400 try: # 开启事务SQLAlchemy 默认自动开启 room Room.query.with_for_update().get(room_id) if room is None: return jsonify({code: 404, msg: 房间不存在}), 404 if room.current_count room.capacity: return jsonify({code: 400, msg: 房间已满}), 400 # 检查学生是否已有活跃住宿记录 existing DormRecord.query.filter_by( student_idstudent_id, statusactive ).first() if existing: return jsonify({code: 400, msg: 该学生已有住宿记录请先退宿}), 400 # 检查床位是否被占用 bed_taken DormRecord.query.filter_by( room_idroom_id, bed_numberbed_number, statusactive ).first() if bed_taken: return jsonify({code: 400, msg: 该床位已被占用}), 400 # 创建住宿记录 record DormRecord( student_idstudent_id, room_idroom_id, bed_numberbed_number, check_in_datedate.today(), statusactive ) db.session.add(record) # 更新房间人数 room.current_count 1 db.session.commit() return jsonify({code: 200, msg: 分配成功}) except Exception as e: db.session.rollback() return jsonify({code: 500, msg: f系统错误{str(e)}}), 500这段代码有几个关键点。with_for_update()是行级锁在查询房间时锁住这一行防止两个宿管员同时给同一个房间分配床位导致超员。db.session.commit()提交事务如果中间任何一步出错rollback()回滚所有操作保证数据一致性。调宿逻辑类似先退掉原床位再分配新床位两步操作放在同一个事务里dorm_bp.route(/api/transfer, methods[POST]) login_required role_required(admin, manager) def transfer_dorm(): data request.get_json() student_id data.get(student_id) new_room_id data.get(new_room_id) new_bed data.get(new_bed_number) try: # 查找当前活跃记录 old_record DormRecord.query.filter_by( student_idstudent_id, statusactive ).with_for_update().first() if not old_record: return jsonify({code: 404, msg: 未找到住宿记录}), 404 # 释放原床位 old_room Room.query.with_for_update().get(old_record.room_id) old_room.current_count - 1 old_record.status checked_out old_record.check_out_date date.today() # 分配新床位 new_room Room.query.with_for_update().get(new_room_id) if new_room.current_count new_room.capacity: db.session.rollback() return jsonify({code: 400, msg: 目标房间已满}), 400 new_record DormRecord( student_idstudent_id, room_idnew_room_id, bed_numbernew_bed, check_in_datedate.today(), statusactive ) db.session.add(new_record) new_room.current_count 1 db.session.commit() return jsonify({code: 200, msg: 调宿成功}) except Exception as e: db.session.rollback() return jsonify({code: 500, msg: str(e)}), 500调宿时如果目标房间满了直接 rollback原记录的状态变更也会被撤销不会出现学生被退宿但没分配到新房间的情况。4.3 报修流程与状态流转报修功能相对简单学生提交报修单宿管员接单处理student_bp.route(/api/repair, methods[POST]) login_required def submit_repair(): data request.get_json() content data.get(content, ).strip() if not content or len(content) 5: return jsonify({code: 400, msg: 报修内容不能少于5个字}), 400 # 获取学生当前住宿记录 record DormRecord.query.filter_by( student_idcurrent_user.id, statusactive ).first() if not record: return jsonify({code: 400, msg: 您当前没有住宿记录}), 400 repair RepairRequest( student_idcurrent_user.id, room_idrecord.room_id, contentcontent, statuspending ) db.session.add(repair) db.session.commit() return jsonify({code: 200, msg: 报修提交成功}) dorm_bp.route(/api/repair/int:repair_id/process, methods[PUT]) login_required role_required(admin, manager) def process_repair(repair_id): repair RepairRequest.query.get_or_404(repair_id) if repair.status done: return jsonify({code: 400, msg: 该报修已处理完成}), 400 repair.status done repair.resolved_at datetime.utcnow() db.session.commit() return jsonify({code: 200, msg: 处理完成})报修状态用 Enum 限定三个值前端根据状态显示不同颜色的标签。pending 显示红色processing 显示黄色done 显示绿色。这个细节在答辩演示时很加分老师能看到你对状态管理的理解。5. 避坑与排查那些让我熬夜到凌晨三点的错误5.1 中文乱码从数据库到页面的全链路排查现象学生姓名存进去是正常的但页面上显示成问号或者乱码。原因字符集不统一。MySQL 数据库、表、连接、Python 文件编码、HTML 页面编码任何一环不是 utf8mb4 都会出问题。解决按顺序检查。第一数据库和表的字符集必须是 utf8mb4用SHOW CREATE TABLE users;查看。第二连接串里必须带?charsetutf8mb4。第三Python 文件头部加# -*- coding: utf-8 -*-。第四HTML 模板里加meta charsetUTF-8。第五如果用了 Flask 的 jsonify确保app.config[JSON_AS_ASCII] False否则中文会被转义成 \uXXXX 格式。5.2 数据库连接超时Lost connection to MySQL server现象项目跑了一晚上第二天早上来访问报Lost connection to MySQL server during query。原因MySQL 默认的wait_timeout是 28800 秒8小时连接空闲超过这个时间就被服务端断开了。但 SQLAlchemy 的连接池不知道连接已经失效取出来直接用就报错。解决在SQLALCHEMY_ENGINE_OPTIONS里加pool_pre_ping: True和pool_recycle: 3600。pool_pre_ping让每次取连接前先 ping 一下断了就重连。pool_recycle让连接最多活 3600 秒就被回收重建双保险。5.3 外键约束报错Cannot add or update a child row现象删除一个学生时报错提示外键约束失败。原因DormRecord 表里有这个学生的住宿记录外键指向 User 表。直接删 User 记录DormRecord 里的引用就悬空了数据库不允许。解决两种方案。一是级联删除在定义外键时加ondeleteCASCADE删用户时自动删关联记录。二是软删除User 表加is_deleted字段删除时只标记不真删。毕业设计建议用软删除数据保留完整答辩时老师问起来也好解释。5.4 并发分配床位导致超员现象两个宿管员同时给同一个房间分配床位房间容量 4 人结果住进去 5 个人。原因两个请求同时读到current_count 3都判断没满都执行了current_count 1最后变成 5。解决用with_for_update()加行级锁或者用数据库层面的乐观锁。在 Room 表加version字段每次更新时UPDATE rooms SET current_count current_count 1, version version 1 WHERE id ? AND version ?如果影响行数为 0 说明版本冲突重试或报错。毕业设计用with_for_update()就够了简单直接。5.5 Flask 调试模式下代码修改不生效现象改了 Python 代码刷新页面还是旧逻辑。原因Flask 的 debug 模式有自动重载功能但有时候文件监控会失效特别是在 Windows 上。解决手动重启 Flask 服务。如果经常遇到检查是不是用了app.run(debugTrue)但没设置use_reloaderTrue。另外如果项目里有语法错误重载会失败但不会报错需要看控制台输出。最稳妥的办法是改完代码后 CtrlC 停掉再重新python app.py。6. 从能跑到能答辩三个让项目加分的技术细节6.1 用 ECharts 做宿舍入住率可视化答辩时老师最喜欢看图表。在管理员首页加一个入住率饼图和楼层分布柱状图用 ECharts 实现数据从后端 API 拿dorm_bp.route(/api/stats/occupancy) login_required role_required(admin, manager) def occupancy_stats(): buildings Building.query.all() result [] for b in buildings: total Room.query.filter_by(building_idb.id).with_entities( db.func.sum(Room.capacity) ).scalar() or 0 used Room.query.filter_by(building_idb.id).with_entities( db.func.sum(Room.current_count) ).scalar() or 0 result.append({ name: b.name, total: total, used: used, rate: round(used / total * 100, 1) if total 0 else 0 }) return jsonify({code: 200, data: result})前端用 ECharts 渲染饼图显示各楼栋入住率柱状图显示每层楼人数分布。这个功能代码量不大但视觉效果很好论文里也可以放截图。6.2 用 Flask-Migrate 管理数据库版本毕业设计论文里如果提到数据库版本管理会显得很专业。Flask-Migrate 基于 Alembic可以追踪每次表结构变更pip install flask-migrate # 初始化 flask db init # 生成迁移脚本 flask db migrate -m add visitor_logs table # 应用迁移 flask db upgrade每次改模型后执行migrate和upgrade数据库结构自动同步。回滚用flask db downgrade。这个工具在实际工作中也常用写进论文的“系统设计”章节很合适。6.3 部署到服务器Gunicorn Nginx 最小配置本地跑通后如果想把系统部署到服务器上演示用 Gunicorn 做 WSGI 服务器Nginx 做反向代理pip install gunicorn # 启动 4 个 worker 进程 gunicorn -w 4 -b 127.0.0.1:8000 app:create_app()Nginx 配置server { listen 80; server_name your_domain.com; location / { proxy_pass http://127.0.0.1:8000; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; } location /static/ { alias /path/to/dormitory_system/static/; } }Gunicorn 的-w 4表示 4 个 worker 进程一般设置为 CPU 核心数 × 2 1。Nginx 处理静态文件动态请求转发给 Gunicorn。这个部署方案虽然简单但已经能支撑几百人同时在线毕业设计演示绰绰有余。我自己的习惯是每次改完模型先flask db migrate生成迁移脚本看一眼脚本内容确认没问题再upgrade避免自动生成的脚本删错字段。这个习惯帮我省过好几次后悔药。希望帮到你。本文还有配套的精品资源点击获取