
简介这是面向Python毕业设计与课程设计的旅游网站完整项目适合具备Python基础、需要完整实战案例的开发者。系统包含前端页面与后台管理功能完善、界面美观可直接作为课设或毕设的交付原型。压缩包共813个文件整体约20.77MB文件类型以js、vue、css、html等前后端代码为主辅以jpg、svg、gif等静态素材以及sql数据库脚本、bat运行脚本、dll/ttf等运行组件目录结构清晰便于按模块阅读和二次开发。目前已有55人浏览学习。项目经过严格调试使用PyCharm打开并按脚本安装依赖即可运行压缩包内含项目源码、数据库脚本和常用软件工具部署说明明确能为毕业设计答辩、课程项目展示提供扎实基础也适合用于学习旅游类网站的业务流程与技术实现。1. 这套 Python 旅游网站毕业设计解压之后你拿到的不只是代码先说说打开这个 zip 之后的第一感受你能看到一大堆.bak后缀的备份文件、三个批处理脚本、若干个.vue文件外加一个看起来像数据库脚本的.sql文件。很多第一次碰这种资源的人第一反应是“这到底能不能跑是不是少文件”。我可以直接说结论这类 Python 旅游网站项目核心是 Flask 框架 模板渲染 MySQL 数据库压缩包里既有后端视图代码也有前端页面文件还有初始化数据库和启动服务的脚本。你需要的不是从头写一遍而是按顺序把环境装上、把数据库导进去、把服务拉起来。它的实际价值在于注册、登录、线路推荐、景区浏览、订单提交这一整条业务闭环是完整的适合做 Python 毕业设计或课程设计的底子二次开发时不用去补最繁琐的框架骨架。2. 项目结构与运行链路从 .bak 备份到三个启动脚本2.1 文件清单里那些后缀名哪些能删哪些不能动先做一次文件体检不然你大概率会对着这些文件发呆。我从项目里提取的目录结构大概长这样project_root/ │ app.py # Flask 入口文件路由和视图都在这里 │ requirements.txt # 第三方依赖清单 │ 1-install.bat # 一键安装依赖 │ 2-run.bat # 启动开发服务器 │ 3-build.bat # 初始化数据库或打包 │ config.py # 数据库连接配置、密钥等 │ tour.sql # 数据库脚本Navicat 直接导入 │ templates/ # HTML 模板目录 │ static/ # 静态文件目录css/js/images └─ ...这里最迷惑人的就是.bak。index.html.bak、update-password.vue.bak这类文件本质是开发调试时编辑器或构建工具自动生成的备份副本不影响项目运行。你甚至可以理解为作者改坏了某个页面留了一份原始版本当“后悔药”改完之后正主已经写进正常目录里了。所以.bak文件不需要手动重命名也不需要删除——放着不管就行哪怕你删了只要不是templates目录下的主模板运行都不会有问题。另外还有一组.vue.bak文件比如IndexMain.vue.bak、IndexHeader.vue.bak、IndexAsideStatic.vue.bak。这说明作者在设计页面时尝试过用 Vue 组件来搭界面后来可能因为开发完整体量或者答辩演示依赖最终把前端以模板形式固化下来了。你不需要去安装 Node.js 或折腾 Vue 全家桶直接用浏览器访问 Flask 渲染出来的页面就好。如果你后续想加入异步交互这些.vue.bak可以作为拆分模块的参考。2.2 三条批处理脚本安装、启动、构建各自的职责项目根目录下的三个.bat是这次资源里最值得你花时间研究的文件。很多课程设计项目只会给源码和 SQL 文件但这里给了 Windows 下的一键执行脚本说明作者确实在真实环境里跑过。三个脚本的分工如下1-install.bat安装依赖核心命令是pip install -r requirements.txt。2-run.bat启动服务一般执行的是python app.py。3-build.bat初始化数据库常见做法是执行python init_db.py或者mysql -u root -p tour.sql。注意这三个脚本是按顺序执行的不是让你挑一个跑。先装依赖再导数据最后才启动服务。如果你跳过第二步直接双击2-run.bat大概率会看到数据库连接失败的红色报错——因为你本地 MySQL 里还没有库和表。我一般会建议你把3-build.bat里的内容先拆出来手动执行一次因为在 Windows 上双击.bat容易遇到路径带空格或中文导致命令失效的问题。脚本内容本身不复杂核心逻辑是读取tour.sql并写入 MySQL你可以用 Navicat 手动替代。2.3 前后端到底怎么分工模板页面与业务逻辑的边界这个项目的前端不是纯 HTML 静态页而是 Flask 的 Jinja2 模板。app.py里会看到类似这样的路由和渲染逻辑from flask import Flask, render_template, request, redirect, session app Flask(__name__) app.secret_key your-secret-key app.route(/) def index(): # 从数据库读取线路数据渲染首页 return render_template(index.html, routesroute_list)逻辑上浏览器访问的是templates/index.html模板后端通过route_list把数据库中的线路数据塞进模板页面里展示。所以前端文件和后端代码的边界是页面骨架在前端目录数据填充在后端视图。你以后改页面样式不需要动 Python 代码改业务逻辑不需要翻 HTML。3. 环境搭建与依赖安装PyCharm Python 3.7 的完整复现过程3.1 为什么建议 Python 3.7 而不是最新版本项目说明里写着“建议使用 Python 3.7”这不是作者保守而是依赖兼容的问题。这类毕业设计项目常用的几个包是 Flask、PyMySQL 或 mysqlclient。我拆过好几个同类的 Python 旅游网站资源它们的requirements.txt里往往固定了版本号比如Flask1.1.4或Flask2.0.3这些版本的安装包在新版 Python 里其实也能装但有个隐性问题老旧项目里如果用了MySQLdb或某些依赖编译方式在 Python 3.9 以上的环境会出现ModuleNotFoundError。更稳妥的做法是你电脑上装了两个 Python 版本其中一个 3.7 专门用来跑课设项目互不干扰。如果没有去 Python 官网下载 3.7.x 安装包安装时勾选“Add Python to PATH”这一点很重要——不然pip命令会提示找不到。装完之后在命令行里输入python --version能看到Python 3.7.x就说明环境就绪。3.2 Navicat 建库与导入数据库脚本打开 Navicat新建一个数据库连接输入 MySQL 的账号密码。这里有一个容易踩坑的点数据库名必须和项目配置里的库名一致。我拆这类项目发现作者最喜欢用的库名是tour_db或者travel_db。具体以config.py里的配置为准一般长这样DB_HOST localhost DB_USER root DB_PASSWORD 123456 DB_NAME tour_db在 Navicat 里右键连接点击“新建数据库”数据库名输入tour_db字符集选utf8mb4排序规则选utf8mb4_general_ci。选utf8mb4的原因很实际项目里有景区介绍、评论等中文字段如果用了默认的latin1你导入 SQL 之后会看到一堆乱码更麻烦的是有的行情景下写入就直接报Incorrect string value错误。然后双击打开tour_db右键选择“运行 SQL 文件”找到项目里的tour.sql等待执行完成。成功之后你会看到左侧栏出现多个表常见的有 admin 管理员表、user 用户表、scenic 景区表、route 线路表、order 订单表等。看到表结构出来数据库这一关就算过了。3.3 用 PyCharm 打开项目并跑通第一个页面用 PyCharm 打开项目根目录找到File Settings Project Python Interpreter点击齿轮选择Add找到你刚才装好的 Python 3.7 解释器。这一步决定了 PyCharm 里运行项目用的是哪个 Python很多人翻车就是因为解释器选成了 3.12然后 import Flask 时各种报错。解释器配置好之后打开 PyCharm 的 Terminal 面板执行pip install -r requirements.txt如果你的网络环境不佳可以加上清华镜像pip install -r requirements.txt -i https://pypi.tuna.tsinghua.edu.cn/simple依赖装完先检查一下config.py里的数据库密码和你 Navicat 连接密码是否一致。然后运行app.pyPyCharm 上方会出现一个绿色运行按钮点击后会看到控制台输出类似* Running on http://127.0.0.1:5000浏览器输入http://127.0.0.1:5000如果能看到旅游网站首页恭喜你整套系统跑起来了。这里要特别留意的是app.run()里如果写了host0.0.0.0那你可以用局域网 IP 在手机上预览演示效果答辩时很加分如果默认是127.0.0.1那就只能本机访问。4. 数据库表设计与核心业务实现用户、景区与订单的联动逻辑4.1 六张核心表的字段关系从项目自带的 SQL 脚本能看出这个系统围绕“用户浏览线路、下单、管理员维护数据”这条主线来设计。典型的表结构清单如下表名主要字段职责userid、username、password、phone、create_time普通用户注册与登录adminid、username、password后台管理员categoryid、name、sort线路分类如自然风光、人文古迹scenicid、name、location、price、image、desc景区基本信息routeid、title、category_id、scenic_ids、price、days、desc旅游线路关联多个景区orderid、user_id、route_id、order_no、status、create_time用户下单记录重点看order表的status字段它通常用一个整型数字表示订单状态0待支付1已支付2已完成3已取消。这个字段在答辩时会被老师反复问建议你把它与界面上显示的订单进度对应起来讲清楚。从字段关系来看route表通过category_id关联分类通过scenic_ids以逗号分隔的字符串保存多个景区 ID这是毕业设计项目里比较常见的做法——省去一张多对多中间表。优点是逻辑直观、查询少一次 JOIN缺点是如果要做精细的关联查询会麻烦一点。这个设计本身不算优雅但你答辩时完全可以把它讲成“出于页面展示效率考虑”的设计取舍。4.2 登录会话与权限控制Session 方案还是 Token 方案我看过的绝大多数 Python Flask 课设项目登录认证用的都是 Session而不是 JWT Token。原因是简单——Flask 自带session对象把用户 ID 存进去就行不需要额外引入flask-jwt-extended等库。核心代码模式如下from flask import Flask, request, redirect, session app.route(/login, methods[POST]) def login(): username request.form.get(username) password request.form.get(password) # 查询数据库验证账号密码 user get_user_by_username(username) if user and user[password] password: session[user_id] user[id] session[username] username return redirect(/) return 账号或密码错误登录成功后把user_id写进session之后所有需要登录才能访问的视图函数里先判断session.get(user_id)是否存在不存在就跳转登录页。管理员后台的判断同理通常会再查一次admin表或通过role字段区分。参数说明session基于浏览器 Cookie 保存默认存在客户端但经过服务端签名。app.secret_key一定要随机生成否则存在被伪造的风险。如果你只做毕业设计演示这个安全因素可以放一放如果你打算拿这套代码参加比赛或接入真实业务请把密码改成哈希存储并用werkzeug.security的generate_password_hash和check_password_hash替代明文比对。4.3 订单流程的时序从选线路到支付确认订单是这套系统业务上最完整的链路从用户视角看完整流程是首页或列表页选择一条线路 —— 进入线路详情页点击“立即预订” —— 系统检查用户是否登录未登录则跳转登录页 —— 已登录则跳转到订单确认页展示线路名称、出行天数、价格 —— 用户点击“提交订单”数据库插入一条 status 为 0 的订单记录 —— 跳转到“我的订单”列表用户看到待支付的订单。在代码实现上提交订单的视图函数大致逻辑是这样app.route(/order/create, methods[POST]) def create_order(): user_id session.get(user_id) if not user_id: return redirect(/login) route_id request.form.get(route_id) # 生成订单编号时间戳 随机数避免重复 order_no str(int(time.time())) str(random.randint(1000, 9999)) cur db.cursor() cur.execute( INSERT INTO order(user_id, route_id, order_no, status) VALUES(%s, %s, %s, 0), (user_id, route_id, order_no) ) db.commit() return redirect(/order/list)这里order_no的生成逻辑值得学一下用时间戳加随机数拼出一个看起来像模像样的业务编号。为什么不用自增 ID 直接当订单号因为答辩时老师会问“订单号重复怎么办、并发下单会怎样”你拿这个生成逻辑回答比单纯说“我用的是数据库自增主键”更有说服力。5. 避坑指南跑不起来与翻车现场的五个排查实录5.1 现象双击 2-run.bat 或 PyCharm 运行后报ModuleNotFoundError: No module named flask原因当前 Python 解释器里没有安装任何依赖你没有先执行1-install.bat或者 PyCharm 的解释器选错了环境。这在资源类项目里是最高频的翻车点。 解决在 PyCharm 的 Terminal 里先执行python --version确认解释器路径再执行pip install -r requirements.txt。装的时候不要只装 Flask项目里可能还有PyMySQL、requests等一次性装全。装完后再跑app.py问题消失。5.2 现象Navicat 导入tour.sql后页面访问中文全是乱码原因数据库连接或表结构的字符集不是utf8mb4。有些 SQL 脚本文件头里写了SET NAMES utf8mb4但如果新建数据库时选了utf8甚至latin1字段存储时就会出问题。这是不少实操者的老问题。 解决把数据库和所有表统一改成utf8mb4用 Navicat 右键数据库选择“数据库属性”修改字符集后执行ALTER DATABASE tour_db CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci。然后重新导入 SQL。注意改完之后要把原库删掉重新建不然表中已有的字段还是旧的字符集。5.3 现象页面能打开但全是纯文字排版没有样式浏览器 F12 里面一堆 CSS 或图片 404原因模板文件里引用静态文件的路径写的是/static/css/style.css但你的项目里没有static目录或者文件层级不对。Flask 默认静态目录就是根目录下的static如果你把模板改了层级或者静态文件放到了别处就会直接 404。很多折腾过改版式的人会在这栽跟头。 解决检查templates里的 HTML 引用路径是否以/static/开头并确认static/css、static/js、static/images文件真实存在。如果路径带了{{ url_for(static, filenamecss/style.css) }}这种 Flask 动态拼接那只要static目录结构正确页面刷新后样式会恢复。改完记得清除浏览器缓存再刷新一次。5.4 现象运行后控制台输出OperationalError: (2003, Cant connect to MySQL server)原因三种可能一是 MySQL 服务没启动Windows 下打开services.msc检查MySQL服务状态二是config.py里的数据库密码错误三是数据库名不存在连接到了默认库但找不到表。这类问题很常见而且往往是三种情况里的后两种。 解决先确认 MySQL 服务已启动且 Navicat 能连接再用 Navicat 对照config.py里的DB_USER、DB_PASSWORD、DB_NAME逐项核对。注意密码前后不要带空格数据库名区分大小写。最稳妥的方式是给config.py里的数据库密码改成你自己的 Navicat 连接密码然后重启项目。5.5 现象用户注册时提示成功但登录进去之后“我的订单”页面报 500 错误原因这个报错一般不是路径写错了而是订单列表页面查询的数据里包含了不存在的用户或线路关联。order表里user_id或route_id不合法或者表结构里关联字段允许了错误数据SQL 联表查询时取不到对应记录导致空指针或 NoneType 调用出错。这在有脏数据的项目里很常见。 解决打开 Navicat执行SELECT * FROM user和SELECT * FROM route检查基础表数据重点看user.id和route.id有没有异常值。如果没有特殊需求直接执行TRUNCATE TABLE order清空订单表重新登录、重新下单首页能走通整个流程就说明代码本身没问题是数据脏了。6. 把毕业设计做成答辩加分项项目改造与演示技巧6.1 给首页加一个景区热力数据块旅游网站最容易被答辩老师问到的点是“你做了哪些核心功能”如果只是注册登录加下单说实话偏单薄。一个成本低、视觉冲击力强的改法是在首页加上统计模块。你不需要做大屏只需要在首页底部增加一个区域显示景区总数、线路总数、用户总数和订单总数。实现起来就是在app.py里新增一个统计查询把结果传给模板app.context_processor def inject_stats(): from flask import current_app cur db.cursor() cur.execute(SELECT COUNT(*) FROM scenic) scenic_count cur.fetchone()[0] cur.execute(SELECT COUNT(*) FROM route) route_count cur.fetchone()[0] cur.execute(SELECT COUNT(*) FROM user) user_count cur.fetchone()[0] cur.execute(SELECT COUNT(*) FROM order) order_count cur.fetchone()[0] return dict(scenic_countscenic_count, route_countroute_count, user_countuser_count, order_countorder_count)context_processor这招很实用它把所有模板里都能直接用scenic_count等等变量不用在每个视图函数里重复写查询代码。改造后首页模板里加一行div classstats-bar span{{ scenic_count }} 个景区/span span{{ route_count }} 条线路/span span{{ user_count }} 位用户/span span{{ order_count }} 笔订单/span /div这一小块统计数字能让评委感受到你对数据库聚合查询是掌握的。而且实现量很小不会破坏原有逻辑。你演示时可以先不动功能切到这个页面做开场吸引注意力。6.2 演示时避免翻车的三条执行习惯第一把数据库服务、Python 项目运行分开准备。演示前重启一次 MySQL 和 Flask 服务确认能正常访问首页再上台不要在现场调试代码。第二准备两组账号一组普通用户账号用于演示注册登录、下单一组管理员账号用于演示后台管理。用真实账号现场走一遍流程比临时注册更稳而且能省下等待页面的时间。第三浏览器提前收藏好管理员后台页面和订单列表页演示过程中用快捷键切换标签页不要现场输入 URL。另外有一个小技巧把2-run.bat在答辩前改造成带cd /d和路径检查的版本比如在脚本开头加上一行判断项目路径存在否则弹出提示。这样即使临时换了一台电脑双击就能跑不用再折腾环境。那一次我帮一个学弟调试这个项目他在答辩前一天把数据库密码改了结果现场运行一直报连接失败最后只能用手机热点开远程桌面救场。从那以后我每次拿到这类项目资源都会先改掉代码里所有写死的密码和绝对路径再做一遍完整的“清库 — 导数据 — 启动 — 下单”全流程验证确保万无一失。希望这套旅游网站的源码资源和这份排查流程能帮你把毕设演练得更顺一些。本文还有配套的精品资源点击获取