基于Flask和Vue的旅游攻略系统设计与实现:从数据库到前后端联调全指南

发布时间:2026/10/21 22:21:30

基于Flask和Vue的旅游攻略系统设计与实现:从数据库到前后端联调全指南 每年到了毕业设计季我总能在各种技术群里看到同一个类型的题目“基于XX框架的XX系统的设计与实现”。最近被问得最集中的是“基于flask框架和python的的张家口旅游攻略系统的设计与实现-vue pycharm django”这个题目。先别急着吐槽标题里Flask和Django同框的奇妙组合也不难理解很多同学选题的时候就是把看过的关键词全堆上去想着多写几个显得工作量饱满。但实际上这个题目里的水挺深技术选型、架构思路、前后端交互、答辩要讲的东西每一项都能单独展开说半天。我决定把这篇文章写成一整篇可抄作业的实操总结从需求拆解开始一步步说清楚架构怎么搭、数据表怎么设计、核心功能怎么实现、前后端怎么调通再把我在类似项目里踩过的坑和排查经验全部整理出来。无论你是拿这个题目做课程设计、毕业设计还是想把它改造成自己的作品集项目这篇文章都能给你一个完整且靠谱的落地参考。1. 先把这个题目读明白需求拆解与系统边界很多人拿到题目就直接开敲代码这是第一个坑。像“张家口旅游攻略系统”这种题目表面上是个信息展示站但如果你没把需求边界理清楚很容易做出一个“只有列表页和详情页”的玩具项目答辩时被老师问两句就接不上话。1.1 这类旅游系统到底要做什么我帮一个学弟做过同样的题目当时我们开需求会的第一件事就是把用户场景列清楚。一个旅游攻略系统站在使用者角度无非就三类角色游客未登录用户浏览景点、看攻略、看路线推荐。注册用户发布了攻略、收藏景点、评论互动、管理个人发布的内容。管理员审核攻略、管理景点数据、管理用户、统计访问情况。别小看这个划分它直接决定你系统要有几个模块。我记得见过很多半成品项目只做了“管理员上传景点前端展示”用户注册登录都没做全这就没达到“系统”的要求。我当时给学弟的设计方案里功能模块是这么切的用户模块注册、登录、个人信息、我的收藏、我的攻略。景点模块景点列表、景点详情、景点分类筛选、景点搜索。攻略模块攻略发布、图文编辑、攻略列表、攻略详情、攻略审核。旅游路线模块推荐路线展示、路线详情。评论与收藏模块对景点的收藏、对攻略的评论和点赞。后台管理模块管理员登录、景点管理、攻略审核、用户管理、数据统计面板。这个功能列表看起来很长但实际做下来每一个模块都不算重关键是你要能讲清楚每个模块之间的数据关系。比如“攻略属于哪个用户”“攻略关联了哪些景点”“用户收藏了哪些景点”这些关系在设计数据表之前必须想明白。1.2 题目里的技术栈矛盾Flask和Django到底听谁的回到标题本身。“flask框架”“django”同时出现这其实是个典型的关键词堆砌问题。Flask和Django都是Python的Web框架二选一才对。这种矛盾如何处理我和学弟讨论后得出一个比较务实的方案后端用FlaskDjango在文档里不要提或者只在技术对比部分简单提一句为什么没用它。为什么选Flask三点理由系统体量不算大Flask轻量灵活写起来快。课程设计/毕业设计需要你能对着代码讲清楚每一步Flask的请求处理链路比Django的“全家桶”模式更容易讲透。配合Vue做前后端分离Flask只需要提供JSON接口不需要模板渲染能力这正好避开了Django的长处。当然如果你的导师要求必须用Django也不是不行换汤不换药核心的数据库设计和接口逻辑基本通用。这篇文章后面我会按照“Flask Vue”这条线来展开。2. 技术选型与开发环境给你一套不折腾的落地组合定好Flask之后剩下的配套选型其实也有讲究。别一上来就装一堆库我建议按照下面的组合来搭后端框架Flask 2.xLTS版本优先数据库MySQL本地环境或SQLite演示环境ORMSQLAlchemy Flask-SQLAlchemy认证方案Flask-JWT-Extended文件上传Flask-Uploads或自己写上传接口前端框架Vue 3 Element Plus Axios构建工具Vite开发工具PyCharm Professional或Community VS Code都行PyCharm在标题里出现了我就多说一句。PyCharm Community版完全够用但很多人不知道要配置虚拟环境直接把项目扔进全局Python里跑最后依赖冲突到怀疑人生。我个人的习惯是在PyCharm里New Project的时候选择VirtualenvPython版本固定用3.9或3.10不要追新用3.13因为部分库的兼容性会让你多花三小时。前端这块用Vite初始化的Vue 3项目比Vue CLI舒服很多启动速度快配置也直观。Element Plus的组件库做后台管理界面特别顺手但你如果只是想把列表和表单页做出来不需要重新造轮子。还有一个重点是目录结构。我看过很多学生项目把所有.py文件堆在一个文件夹里过两天自己都找不着哪个文件对应哪个功能。我习惯的分层结构是这样tour_guide_system/ ├── backend/ │ ├── app.py # Flask应用入口统一注册蓝图 │ ├── config.py # 配置文件包含数据库连接等 │ ├── models/ # 数据库模型 │ │ ├── user.py │ │ ├── scenic.py │ │ └── article.py │ ├── resources/ # 接口蓝图路由 │ │ ├── auth.py │ │ ├── scenic.py │ │ └── article.py │ ├── utils/ # 通用工具函数 │ ├── uploads/ # 上传的图片存储目录 │ └── requirements.txt └── frontend/ ├── src/ │ ├── api/ # 接口请求封装 │ ├── views/ # 页面组件 │ ├── router/ │ └── main.js └── package.json这个结构最大的好处是后端按“模型-接口-工具”拆分前端按“接口-页面-路由”拆分答辩时老师问什么你能快速指出文件位置印象分会好很多。3. 数据库设计旅游系统的数据模型怎么建模才合理先设计数据表再写接口这个顺序一定不能反。我见过太多项目是先写接口写不下去了才回头补表结果表结构乱七八糟查询逻辑越写越痛苦。3.1 核心数据表结构我设计的表结构大概长这样每个表都尽量做到字段精简但覆盖完整用户表userid主键自增username用户名唯一password_hash密码哈希用werkzeug的generate_password_hashavatar头像URLrole角色user/admincreated_at注册时间景点表scenicidname景点名称city所属区县张家口有桥西、桥东、张北、崇礼等这个字段可以做成分类筛选维度category类型自然风光、历史遗迹、滑雪场…description文本介绍cover_image封面图URLlat / lng经纬度如果后面要加地图展示的话is_hot是否推荐攻略表articleidtitle标题content正文cover_image封面图author_id外键关联user表scenic_id外键关联景点表一篇攻略可以关联一个主要景点status状态draft/published/rejectedviews浏览量计数created_at / updated_at收藏表favoriteiduser_id外键scenic_id外键created_at评论表commentiduser_idscenic_id或article_id用type字段区分是对景点还是对攻略的评论contentcreated_at注意收藏表不要只用一个“用户表里存收藏id列表”的方式虽然那样实现简单但后期你想做“判断这个用户是否已收藏”会非常难受。一张独立的收藏表配合联合唯一索引unique(user_id, scenic_id)一条SQL就能查出所有状态。3.2 数据库设计的几个关键细节第一密码一定不要明文存。我在帮学生看代码的时候见过直接把用户密码存数据库的这是大忌。用werkzeug.security的generate_password_hash和check_password_hash代码就两行安全等级完全不同。第二时间字段统一用DateTime类型但前端展示时要格式化。Vue端可以用dayjs或者干脆写一个格式化函数不然你会看到时间显示成“2025-02-20T08:30:00”这种默认格式挺丑的。第三外键关系一定要用SQLAlchemy的relationship配置好。比如查询一个攻略时需要通过author_id拿到作者的用户名如果你没有配置关系就只能手动再查一次用户表增加一次IO。配置好关系之后直接在序列化的时候用article.author.username就能取到代码清爽很多。第四MySQL连接记得在连接串里带上编码参数。用SQLAlchemy连接MySQL时加上?charsetutf8mb4否则中文容易乱码。SQLite没这个问题但SQLite的并发性能比较有限如果老师的验收环境是多人同时演示MySQL会更稳妥。下面给一个简化的模型代码片段方便照抄from flask_sqlalchemy import SQLAlchemy from werkzeug.security import generate_password_hash, check_password_hash from datetime import datetime db SQLAlchemy() class User(db.Model): __tablename__ user id db.Column(db.Integer, primary_keyTrue) username db.Column(db.String(80), uniqueTrue, nullableFalse) password_hash db.Column(db.String(256), nullableFalse) avatar db.Column(db.String(256), default) role db.Column(db.String(20), defaultuser) created_at db.Column(db.DateTime, defaultdatetime.now) 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) class Scenic(db.Model): __tablename__ scenic id db.Column(db.Integer, primary_keyTrue) name db.Column(db.String(120), nullableFalse) city db.Column(db.String(50), default) category db.Column(db.String(50), default) description db.Column(db.Text) cover_image db.Column(db.String(256), default) is_hot db.Column(db.Boolean, defaultFalse) created_at db.Column(db.DateTime, defaultdatetime.now) class Article(db.Model): __tablename__ article id db.Column(db.Integer, primary_keyTrue) title db.Column(db.String(200), nullableFalse) content db.Column(db.Text, nullableFalse) cover_image db.Column(db.String(256), default) views db.Column(db.Integer, default0) status db.Column(db.String(20), defaultpublished) created_at db.Column(db.DateTime, defaultdatetime.now) updated_at db.Column(db.DateTime, defaultdatetime.now, onupdatedatetime.now) author_id db.Column(db.Integer, db.ForeignKey(user.id)) scenic_id db.Column(db.Integer, db.ForeignKey(scenic.id)) author db.relationship(User, backrefarticles) scenic db.relationship(Scenic, backrefarticles)我建议你把模型文件拆开放在models目录里不同模块的表分开管理但SQLAlchemy初始化放在公共模块避免循环引用。4. 核心功能实现与前后端联调从登录到攻略闭环数据表设计好之后后端接口的开发就是一个“翻译”过程把文档里要的功能翻译成对应的接口。这一阶段我最想强调的是接口返回格式一定要统一。4.1 统一返回格式和认证方案我推荐的统一返回结构是这样{ code: 0, message: success, data: { ... } }所有接口都走这个包裹结构code0表示成功非0表示业务错误。前端Axios拦截器里判断code统一弹错误提示这样前后端联调时遇到错误很容易定位。很多新手项目失败就失败在返回格式不统一一会儿直接返回数组一会儿返回对象前端每个页面都得单独处理异常情况。认证方案用JWT。用户登录成功之后后端签发一个token给前端前端把token存在localStorage里每次请求都带在Authorization头里后端写一个装饰器去验证token同时拿到当前用户ID。Flask-JWT-Extended的用法很成熟jwt_required()装饰器直接挂在受保护的路由上就完事。这里我踩过的坑是token过期时间不要太长我一般设成12小时演示的时候够用同时也方便在答辩时演示“token失效后接口返回401”的场景也算一个加分点。注册登录接口的简化版逻辑from flask import Blueprint, request, jsonify from flask_jwt_extended import create_access_token, jwt_required, get_jwt_identity auth_bp Blueprint(auth, __name__) auth_bp.post(/register) def register(): data request.get_json() username data.get(username) password data.get(password) if not username or not password: return jsonify(code400, message缺少用户名或密码, dataNone) if User.query.filter_by(usernameusername).first(): return jsonify(code400, message用户名已存在, dataNone) user User(usernameusername) user.set_password(password) db.session.add(user) db.session.commit() return jsonify(code0, message注册成功, dataNone) auth_bp.post(/login) def login(): data request.get_json() username data.get(username) password data.get(password) user User.query.filter_by(usernameusername).first() if not user or not user.check_password(password): return jsonify(code401, message用户名或密码错误, dataNone) token create_access_token(identitystr(user.id)) return jsonify(code0, message登录成功, data{token: token, username: user.username, role: user.role})4.2 攻略发布功能怎么做攻略发布是这个系统的核心功能之一。难点不在表单而在图片上传和富文本内容。我的建议方案是图片上传单独做一个接口前端先上传图片拿到URL再把URL和正文一起提交。图片上传接口用request.files接收文件保存到后端的uploads目录并把相对路径返回给前端。保存到数据库时只存路径不存文件二进制这是常规做法。一个实用细节是文件名不要直接用原始文件名要用uuid或时间戳重命名否则别人传一个“1.jpg”你就覆盖了。攻略接口这边我建议把“获取攻略列表”“获取攻略详情”“发布攻略”拆成三个接口。列表接口支持关键词搜索、分页、按浏览量排序详情接口访问量加1同时返回作者信息和关联景点信息。我们学弟在实现“详情页浏览量1”的时候遇到一个问题每次刷新页面浏览量都涨其实这是正常的但如果你不想让同一个用户反复刷新刷高浏览量可以加一个简单的session标记限制同一用户5分钟之内只能计一次。这种小细节写进结课报告里老师会觉得你考虑问题很周全。4.3 Vue端关键实现前端这边我按照页面维度拆分首页景点推荐轮播图热门攻略列表景点列表页分类筛选卡片展示景点详情页图文信息评论攻略发布页表单图片上传攻略详情页富文本渲染评论个人中心我的攻略我的收藏后台管理页表格表单弹窗一个核心封装是Axios实例。我在src/api/request.js里统一配置好baseURL和请求拦截器加上token到Authorization头import axios from axios const request axios.create({ baseURL: http://127.0.0.1:5000/api, timeout: 10000 }) request.interceptors.request.use(config { const token localStorage.getItem(token) if (token) { config.headers.Authorization Bearer ${token} } return config }) request.interceptors.response.use(response { const res response.data if (res.code ! 0) { // 统一错误处理 } return res }, error { // 401跳转登录页 })这个封装做完之后业务页面调用接口就非常简洁const response await request.get(/scenic/list, { params: { city: 崇礼, page: 1 } })这里要提醒一个前后端联调时高概率踩坑的点跨域。Flask后端跑在5000端口Vite前端跑在5173端口两者不同源浏览器会拦截。解决办法有两个Flask端加flask-cors扩展一行CORS(app)搞定或者Vite端配置proxy代理。我的建议是两者都配上开发期用Vite代理遇到奇怪的问题时直接开Flask的CORS排查。4.3 富文本编辑器和图片上传的坑如果攻略正文用textarea纯文本体验会比较简陋。我推荐用wangEditor中文文档友好或Tinymce。用富文本编辑器后有个问题编辑器上传图片默认是base64编码塞进HTML里的数据库会变得巨大而且一条攻略正文动辄几万字符。所以你要在编辑器配置里把图片上传指向我们写的上传接口让正文里只保留图片URL。还有一个我建议提前处理的点src路径问题。富文本里的图片路径如果是http://127.0.0.1:5000/uploads/xxx.png前端直接展示没问题如果你只存了相对路径/uploads/xxx.png就要在Vue端配置一个网络图片代理域名或在请求时拼接baseURL。我偷懒的方式是后端返回正文时用正则把/uploads/替换成完整URLcontent content.replace(/uploads/, request.host_url.rstrip(/) /uploads/)这样前端拿到正文后直接绑定到v-html就能正常出图。5. 常见问题排查速查表这项目里我踩过的所有坑这类项目前后端分离问题通常集中在网络请求链路和数据格式上。我整理一张排查表出来每一个都真实发生过你照着自查能省不少时间。问题现象可能原因排查命令 / 解决方式注册成功但登录提示密码错误密码哈希算法不一致检查是否用werkzeug.security生成和校验密码不要自己写hash前端请求接口401token缺失或过期打开浏览器的Network面板看请求头里有没有Authorization前端能打开页面但拿不到数据跨域问题后端加flask-cors或Vite配置proxy看控制台的CORS报错上传的图片404文件路径保存时少了前缀确认uploads目录是否被Flask静态托管用send_from_directory写一个图片访问接口数据库中文乱码连接串没有指定utf8mb4MySQL连接串加参数并确认库/表字符集为utf8mb4前端v-html显示富文本不渲染图片图片是相对路径后端返回前把相对路径替换成完整URL请求返回500后端异常但被统一包裹吞掉了开发环境关闭debug自动重载不应该打开debug并查看终端traceback列表分页数据不对前端传参名和后端参数名不一致统一约定参数名为page和page_size用console.log打印后端收到的值某一个接口很快某一个接口很慢N1查询问题在SQLAlchemy的relationship里设置lazyjoined或使用joinedload关于N1查询多说一句。比如查询攻略列表如果每一条攻略都要查一次作者列表20条数据就会产生21条SQL。SQLAlchemy提供了查询优化工具用joinedload一次性把关联查出来from sqlalchemy.orm import joinedload articles Article.query.options(joinedload(Article.author), joinedload(Article.scenic)).all()这条优化在数据量小的时候看不出来差别但我让你加上的原因依然是答辩时老师可能冷不丁问一句“你的查询有没有性能问题”你能说出N1并给出解决方案这就是一个很亮眼的加分项。后端还有一个所有新手都会犯的错误接口没有做异常处理。比如攻略详情传入一个不存在的id直接article Article.query.get(id)然后取article.title如果article是None就会报AttributeError。健壮的写法是先判断加上统一异常捕获article Article.query.get(id) if not article: return jsonify(code404, message文章不存在, dataNone)这看起来是小事但能保证无论怎么操作前端收到的永远是统一结构的JSON而不是一个巨大的HTML错误页。6. 写在实操之后的几点体会做这个系统的过程里我最深的一个体会是千万不要把“功能能跑”当成目标。同样的题目有人做出来就是课程作业水平有人做出来就是可以放进作品集的项目差距往往不在代码量而在细节设计。我建议你在完成基础功能后优先补上这四件事第一完善管理后台的权限控制。普通用户在URL里输一个/api/admin/scenic就能管理景点这是很严重的安全漏洞。用admin_required装饰器控制好演示时可以演一下“非管理员访问被拒”。第二搜索功能做成模糊搜索。name LIKE %关键词%虽然简单但确实是用户最容易感知到的功能你甚至可以扩展成按景点名称描述城市三个字段一起搜查询范围更广keyword data.get(keyword, ) query Scenic.query if keyword: query query.filter( db.or_( Scenic.name.like(f%{keyword}%), Scenic.city.like(f%{keyword}%), Scenic.description.like(f%{keyword}%) ) )第三做一个简单的基础统计接口。后台首页展示“用户总数、景点总数、攻略总数、评论总数”四个卡片再用SQL语句做分组统计比如最近7天发布的攻略数。这个功能代码量不大但会让系统“厚度”一下子提升。第四写好README。包含环境版本、运行步骤、初始账号、核心接口列表。这个不仅是为了交作业也是为你自己的项目演示省时间。我每次接手帮别人看这类项目最痛苦的就是不知道用什么Python版本、依赖装了哪些、数据库账号密码多少。README写清楚了老师验收时印象分会好很多。我在跨过前面说的那些坑之后用Flask搭后端接口实际只花了两天前端页面做布局交互又花了两天剩下两天全在调富文本图片、跨域、权限这些细节。说实话真正难倒人的从来不是框架本身而是那些“看起来不复杂但永远在出问题”的联调过程。如果你正在做这个题目按我上面的结构一步步来应该能比大多数人少走一半的弯路。
延伸阅读

更多相关文章

2026/10/21 22:16:29

LangGraph实战:构建多Agent分析报告平台

去年年中,我接到一个让我头疼了大半个月的需求:团队攒了一大批零散的调研素材,希望系统能在十分钟内把它变成一份多维度的分析报告——要包含市场判断、技术路线评估和潜在风险提示。一开始,我用了最直觉的方案:把全部…

2026/10/21 22:16:29

从零构建可复用的Skill体系:设计、编排与避坑指南

1. 从“skills”这个标题说起:一个被严重低估的硬核话题“skills”这个词,乍一看平平无奇,甚至有点太泛了。但如果你在技术社区、职场论坛或者个人成长圈子里泡过一段时间,就会发现这个词最近被反复提起的频率高得离谱。有人把它翻…

2026/10/21 22:16:29

基于YOLOv8的脑肿瘤检测系统构建全流程解析

1. 项目定位与方案选型思路1.1 这个项目到底在做什么脑肿瘤检测,本质上是医学影像领域的计算机视觉任务。过去医生在核磁共振图像上看片子,靠肉眼找病灶,费时费力,而且不同年资的医生判断水平差异很大。做一个“基于YOLOv8的脑肿瘤…

2026/10/22 1:52:18

免费学Python:learnpython完整笔记与互动课程资源全解

【免费下载链接】learnpython Python Resources for Everyone 项目地址: https://gitcode.com/gh_mirrors/learnpython5/learnpython 点击查看 免费下载 learnpython 是一个面向所有人的免费 Python 学习资源仓库,把完整的 Python 学习笔记、互动课程、…

2026/10/22 1:52:18

如何快速上手 liKeYun_Ylb:从零跑通微信私域引流系统的完整指南

如何快速上手 liKeYun_Ylb:从零跑通微信私域引流系统的完整指南 【免费下载链接】liKeYun_ylb 引流宝致力于为个人、团队提供基于微信私域流量的推广、引流的效率工具。可减轻人力,有效降低资源损失、流量流失的几率。引流宝完全开源,免费&am…

2026/10/21 4:53:08

金融AI落地指南:从调研报告到技术选型与工程实践

简介:这份PDF报告聚焦2025年金融服务业中国AI现状与趋势,面向金融科技从业者、AI战略规划人员及关注行业数字化转型的研究者,帮助读者把握生成式AI在银行、支付、交易等场景中的落地路径与投资方向。资源包内含1个PDF文件,大小约1…

2026/10/22 0:01:49

电商智能运营体系从0到1:架构设计、落地步骤与避坑指南

前两年我们团队接了一个电商平台的运营中台项目,目标很直白:把过去靠运营拍脑袋做的选品、定价、发券、推活动这些事,逐步用数据算法和自动化流程代替。项目名字内部就叫“电商智能运营体系”。最近有不少朋友问我当时是怎么设计的、踩过哪些…

2026/10/22 0:01:49

若依框架AI代码生成不稳定?用Agent Skill三层结构+五步法搞定

最近一段时间,我一直想把后台管理系统里那些机械重复的增删改查代码交给AI去写。框架用的是若依,按理说这类业务代码模式非常固定,应该是AI最擅长的事情。可真试下来却不是那么回事——你让AI生成一个用户管理模块,它给出的Contro…

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

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

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