ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

基于Django的旅游景点推荐系统:毕设选题、数据库设计与推荐算法

基于Django的旅游景点推荐系统:毕设选题、数据库设计与推荐算法 1. 为什么我说旅游景点推荐系统是毕业设计的安全牌每年带毕设的时候都有人来问我老师这个题目到底好不好做会不会太简单会不会做不完时间紧怎么办如果你是计算机相关专业的学生正在为选题发愁我直接给结论django旅游景点推荐系统是一个难度适中、工作量清晰、展示效果好的经典选题。这个课题特别适合用Django框架来做既能体现你对Web开发基本流程的掌握又能在推荐算法上做出亮点评阅老师看了也不会觉得太水。先说说这类系统的价值在哪。旅游是所有人都能理解的生活场景系统功能一看就懂——用户注册、登录、浏览景点、搜索、收藏、评分、看推荐列表管理员维护景点数据、管理用户评论、查看统计报表。它的功能边界很自然不像某些物联网项目要调硬件也不像纯粹的后端管理系统那样缺乏交互感可以做得看得见摸得着。一台电脑加一套浏览器Cache就能演示完整个流程答辩现场不会出幺蛾子。再说技术层面。Django框架本身是Python Web开发中最成熟的选择之一MTV模式Model-Template-View结构清晰有自带的后台Admin、ORM、表单和认证体系。一个毕业设计用Django来做天然就能把这些核心功能串起来。你不需要去啃复杂的分布式架构也不需要处理跨域、部署高并发这些实际问题这些对毕设来说都是加分项而不是必需品。这个项目的最优解就是把基础功能做得扎实、推荐逻辑讲得清楚、代码结构保持整洁。从实际带学生的经验看这个选题适合这样几类人一是Django刚入门、想通过一个完整项目把框架用熟练的二是时间比较紧迫比如只有8到10周需要快速出成果的三是对推荐系统有好奇、想在毕设里加一点算法成分但又不敢选纯算法的。这个题目给了你一个弹性空间——你可以只做热度排行分类筛选也可以做到基于内容的推荐甚至简单的协同过滤完全看你的精力和野心。这个系统的数据来源也不用太担心。有公开的旅游景点数据集比如国家A级景区名录、一些旅游网站开放的数据接口或者你自己按城市、按类型整理一份模拟数据。实话实说毕业设计的数据规模不需要追求大几百条到一千条景点数据、几十个测试用户就足够支撑整个系统跑起来了。重要的是数据结构设计得合理、数据有区分度这样推荐效果才看得见——不然全是同质化数据再好的算法也白搭。我个人特别看重这个题的可答辩性。评阅老师喜欢问的问题基本都能在系统里找到落脚点表怎么设计的推荐是怎么算的权限怎么控制的搜索怎么做的性能有没有优化这些问题都有明确答案不会答不上来。接下来这篇文章我就把从需求分析、数据库设计、推荐算法落地到开发排期和答辩准备的完整思路全部拆开讲你照着做基本不会走弯路。2. 系统功能边界与数据库表设计先划好能答辩的地盘2.1 用户端 vs 管理端毕业设计的功能红线很多人拿到题目第一反应是功能越多越好其实这是大忌。毕业设计的核心不是堆功能而是每个功能都要能讲出设计理由。旅游景点推荐系统我建议你把功能边界划分得非常清楚分用户端、管理端两块来看。用户端前台要做的事情包括注册与登录、景点列表展示支持分类筛选和关键字搜索、景点详情页包含图片、简介、评分、评论、个人中心收藏列表、我的评分、浏览记录、以及推荐模块——首页的热门推荐和猜你喜欢。这六块是主体每一块都有明确的交互和数据流覆盖了增删改查、关联查询、认证授权这些Web开发的基本考点。管理端后台要做的事情包括景点数据管理增删改查、景点分类管理、用户管理查看/禁用账号、评论审核或删除、以及简单的数据统计比如各分类景点数量、收藏排行、评分分布。如果你用的是Django自带的Admin后台只要把数据模型注册进去这些基础管理功能基本就白送了稍微定制一下页面就能用。这样的工作量分配最合理前台展示你亲自动手写视图和模板后台管理用Django Admin快速搞定两头兼顾效率与可展示性。不建议做的支付、订单、购票、路线规划、地图导航。这些功能要么涉及真实支付接口的高复杂度要么需要接第三方地图SDK对毕设来说风险大于收益。评阅老师不会因为缺少购票功能而扣分但会因为购票功能没做完/有bug而质疑你的项目质量。2.2 数据库表设计核心表与字段一览数据库设计是答辩时必被问到的地方这里我直接给出一套经过验证的表的方案。你不需要改得很花哨稳定、可解释、符合范式就足够。用户表User直接用Django内置的AbstractUser扩展即可额外添加phone手机号、avatar头像URL、bio个性签名这几个字段。用系统自带的认证体系有一个非常大的好处——密码加密、session管理、权限验证都是现成的不用自己造轮子答辩时还能说我复用了Django的用户认证框架保证了安全性。景点表ScenicSpot这是核心表字段一定要设计得有区分度。包括name景点名称、image封面图、description详细描述、category外键关联分类表、city所在城市、address详细地址、price门票价格可以是0表示免费、recommend人工推荐标记、keywords标签用逗号分隔或单独的标签表、heat热度值、avg_rating平均评分、rating_num评价人数、view_count浏览量、created_at创建时间。这里我特别提醒一个容易遗漏的点heat和view_count一定要分开。view_count是实际的浏览点击数heat是你计算出来的热度权重两者不能混为一谈。热度值可以由浏览量、评分、收藏数加权计算出来每次用户行为后更新或者定时任务更新推荐时直接按热度排序。分类表Category字段就两个——name分类名如自然风光、人文古迹、主题乐园、博物馆、description描述。景点表外键关联到这里。评论表Comment核心字段有user外键关联用户、spot外键关联景点、content评论内容、rating评分1到5分、created_at。这里有一个设计决策要注意评论和评分建议放进同一张表因为用户写下评论的同时通常也会打一个分。如果你分开两张表会出现只评分没评论和只评论没评分导致数据不一致推荐算法的评分数据也难聚合。收藏表Favorite用户和景点多对多关系字段就是user、spot、created_at。可以用Django的ManyToManyField加中间表实现。收藏数据是推荐系统里非常重要的隐式反馈——用户收藏了说明他喜欢这类景点这是协同过滤的天然输入。浏览记录表VisitRecord记录用户每次查看景点详情的行为。字段包括user、spot、visited_at。这张表很多人会忽略但它对推荐系统太关键了——猜你喜欢的第一手数据来源就是浏览历史用户的近期浏览类别偏好完全从这里统计。日志表或操作表如果你想在管理后台做统计报表再建一个简单的用户操作日志表记录登录、收藏、评分等动作也行但这不是必须的。如果时间紧把统计做成视图查询对已有表做聚合就足够了。这6张表加上Django默认的权限组表、session表构成了整个系统的数据底座。设计原则就一句话每张表的存在都能讲出一个业务理由每个字段都是后续代码里会用到的没有冗余字段。答辩时把这套表结构讲清楚数据库设计这个环节已经能拿百分之八十的分数。2.3 关键流程一次完整的推荐请求怎么走弄清楚数据表结构之后我们走一遍一次猜你喜欢请求的完整链路这对你写代码时理清思路很有帮助。用户在前端点击猜你喜欢浏览器发送GET请求到/recommend/路由。Django的URL解析器把请求交给对应的视图函数RecommendView。视图函数先做用户验证必须登录才能看到个性化推荐然后分三步取数据从VisitRecord或Favorite表取出当前用户的近期行为记录根据行为记录计算用户偏好的分类和标签在ScenicSpot表里筛选出同分类/同标签、且用户未收藏未浏览过的其他景点按照热度值或其他评分排序返回前N条。视图函数把取到的景点列表传给模板又叫渲染上下文模板用Django模板语言循环展示景点卡片。最后浏览器呈现出带有个性化字样的推荐结果。这条链路不复杂但它展示了你对Django MTV架构、ORM查询、认证系统、模板渲染的理解。答辩时评阅老师可能会问你的推荐结果里为什么能排除用户已经去过的景点这时你就可以从容回答我在视图里加了exclude(id__invisited_id_list)的过滤条件这很体现你对业务细节的思考。3. 推荐功能从能用到好用三步递进不抄复杂算法推荐算法是整个项目最容易被问为什么这样做的地方。我的建议是不要一上来就上协同过滤、矩阵分解那是自己挖坑。正确的策略是由浅入深、一步一步来每一步都能单独成为答辩亮点。3.1 第一步热度加权推荐——先让首页不尴尬最基础也最实用的一版推荐是热度排名。很多毕业设计项目在首页展示热门景点但没有解释这个热门是怎么来的只是按浏览量排个序。你可以在这个基础上加一点公式让它看起来更像推荐。一个简单有效的热度公式heat view_count * 0.3 favorite_count * 0.4 rating_num * avg_rating * 0.15 (1 - days_since_created / 365) * 0.15说人话就是浏览量贡献30%权重收藏数贡献40%收藏比浏览更能体现兴趣评分乘以评分人数贡献15%评分高且评价多说明质量稳定时间衰减因子贡献15%新景点更容易获得曝光。这个公式你可以在Django的ORM里用一个annotate聚合查询来实现或者干脆再创建一个热度字段定时统计更新。# views.py 中的热门推荐查询示例 from django.db.models import F, FloatField, ExpressionWrapper from .models import ScenicSpot hot_spots ScenicSpot.objects.annotate( heat_valueExpressionWrapper( F(view_count) * 0.3 F(favorite_count) * 0.4 F(rating_num) * F(avg_rating) * 0.15, output_fieldFloatField() ) ).order_by(-heat_value)[:10]这段代码答辩时拿出来比直接写order_by(-view_count)要漂亮得多。评阅老师一看就知道你理解了多指标综合排序的思想。这个版本的推荐已经足够让首页不尴尬了——上面展示的景点既有知名度又有好评度还会有一些新景点冒出来。3.2 第二步基于内容的推荐——用标签和分类做猜你喜欢热度推荐是所有人都看到一样的榜单而个性化才是推荐系统的灵魂。第二步我们用基于内容的推荐思想给每个用户算出一个专属列表。基于内容的推荐核心逻辑给每个用户建立一张喜好画像然后拿画像去匹配景点的内容特征。用户的喜好画像来自他的行为——浏览过的景点、收藏过的景点、高分评价的景点。景点的内容特征来自它的分类、所在城市、标签关键词。具体做法分三步从VisitRecord和Favorite表里取出用户最近一段时间的景点ID列表统计这些景点里出现频率最高的分类和城市。比如用户浏览了8个景点其中5个都是自然风光、4个都在成都那他的画像就是自然风光、成都在ScenicSpot表里筛选出符合条件的景点并且排除用户已经收藏或浏览过的用exclude按热度排序返回。代码实现很直接from django.db.models import Count from .models import VisitRecord, Favorite, ScenicSpot viewed_spot_ids VisitRecord.objects.filter(userrequest.user).values_list(spot_id, flatTrue) fav_spot_ids Favorite.objects.filter(userrequest.user).values_list(spot_id, flatTrue) # 统计用户浏览景点的分类偏好 category_count ScenicSpot.objects.filter(id__inviewed_spot_ids) \ .values(category__name) \ .annotate(cntCount(id)) \ .order_by(-cnt) pref_categories [item[category__name] for item in category_count[:2]] recommend_spots ScenicSpot.objects.filter(category__name__inpref_categories) \ .exclude(id__inviewed_spot_ids) \ .exclude(id__infav_spot_ids) \ .order_by(-heat)[:10]这种推荐的解释性极强。答辩时评阅老师问你这个推荐依据是什么你可以直接回答推荐依据是用户历史浏览行为的类别偏好系统提取用户最喜欢的景点类目在同类别里推荐用户没去过的高质量景点。这个回答呼应了真实推荐系统里基于内容推荐的核心思想又完全在自己能实现的代码范围内。3.3 第三步用户评分协同过滤——加分的进阶版如果时间充裕或者在论文里想多写一个章节可以在此基础上加一个基于用户的协同过滤。思想也不难找到和你口味相似的用户把这些人喜欢而你还没看过的景点推荐给你。用余弦相似度计算两个用户的评分向量相似度# 简化版用共同评过分景点的评分向量算余弦相似度 import numpy as np def cosine_similarity(user1_ratings, user2_ratings): common set(user1_ratings.keys()) set(user2_ratings.keys()) if not common: return 0.0 vec1 np.array([user1_ratings[k] for k in common]) vec2 np.array([user2_ratings[k] for k in common]) return float(np.dot(vec1, vec2) / (np.linalg.norm(vec1) * np.linalg.norm(vec2)))先遍历所有用户计算目标用户与其他人的相似度找出相似度最高的K个用户比如K5把这5个人评分最高的景点聚合起来排除目标用户已经看过的按相似度加权评分排序产出推荐。这就是最朴素、可解释的协同过滤不需要任何机器学习库纯Python和Django ORM就能搞定。这里提醒一个现实的坑协同过滤的效果取决于评分数据的丰富程度。如果你的系统里只有十几个测试用户每个人都只评了零星几条相似度计算结果会非常稀疏推荐效果甚至不如基于内容的热度推荐。这不是算法问题是数据问题。建议这样处理如果用户评分数据少于某个阈值比如每个用户平均少于5条评分系统自动降级到基于内容的推荐数据充足时才启用协同过滤。系统能自动判断数据够不够格使用高级算法这就是一个非常好的论文卖点。答辩时加一句我做了数据稀疏性判断使用自适应推荐策略瞬间就和其他人拉开差距。3.4 数据构造没有真实用户数据时怎么让推荐看起来合理毕业设计最大的痛点就是没有真实用户数据。你自己注册三五个测试账号手动评分几十条根本喂不饱协同过滤。我的解决办法是写一个独立的数据库填充脚本模拟30到40个虚拟用户的行为。思路很简单造一个用户字典包含用户名可以带编号、偏好分类列表。然后生成随机行为——有偏好的用户对偏好分类下的热门景点打4到5分、对不喜欢的分类打1到2分、对中间档不评分只看不评。生成完之后把每条行为写入VisitRecord、Favorite、Comment表。这个脚本放在项目根目录的scripts/generate_mock_data.py里用python manage.py shell scripts/generate_mock_data.py执行即可。生成的数据规模建议控制在40个用户、每条评分0到15条、每个用户2到8次浏览、收藏总数200条左右。这样推荐算法跑出来的结果会非常丰富首页、猜你喜欢、相似用户推荐全部有真实数据支撑。更重要的是答辩时如果你能说我编写了一个自动化数据脚本模拟了40个用户的真实行为数据用于测试推荐算法的效果这比去网上爬数据的履历更干净、也更好说明毕竟爬数据涉及数据来源合规是个容易被追问的灰色地带模拟数据反而站得住脚。4. Django项目里最容易翻车的几个细节ORM查询、静态文件与后台管理这部分内容是我看着多少人踩坑之后总结的。功能和表结构再合理如果下面这些细节没处理好项目演示时照样卡壳。4.1 ORM查询的N1问题列表页卡顿的元凶在景点列表页如果直接写ScenicSpot.objects.all()然后循环展示景点并在循环里访问spot.category.nameDjango每次循环都会单独发起一次查询Category表——这就是经典的N1查询问题。数据量小的时候没感觉一旦模拟数据生成1000条景点页面响应时间可能从几十毫秒飙到几秒这在答辩现场非常尴尬。正确做法是使用select_related提前把外键关联查出来spots ScenicSpot.objects.all().select_related(category)返回的QuerySet会通过一次JOIN把关联的Category信息一并取出来循环里访问spot.category.name不再触发额外查询。如果表结构再复杂一点多对多关系比如景点与标签使用prefetch_related预取。你在答辩时可以主动提一句我专门使用select_related优化了列表页的查询性能消除了N1问题评阅老师会觉得你不仅有功能意识也有性能意识。4.2 删除对象时外键关联怎么办on_delete的选择逻辑这是Django新手最容易犯迷糊的问题。删除一个景点时这个景点被用户收藏了、被评论了、被浏览记录了外键关联的数据应该怎么办很多人在Model里随便写on_deletemodels.CASCADE结果删除景点后收藏表、评论表里的数据被连锁删除这是灾难性的。推荐的做法对外键关联设置为on_deletemodels.CASCADE表示用户删了他收藏的数据就删了这是合理的但景点与评论/收藏的关系通常不希望删景点后清空用户的所有相关记录而是希望保留记录、外键置空或采用保护模式。我看过很多毕业设计代码最稳妥的设置逻辑是Comment表里的spot外键设为on_deletemodels.CASCADE。因为景点被删了评论内容就失去了指向对象删除是合理的。Comment表里的user外键同样CASCADE用户注销后评论清空说得通。Favorite表里的spot、user外键CASCADE符合业务逻辑收藏嘛对象没了收藏自然没意义。VisitRecord表的spot、user外键按道理也应该CASCADE但如果你要做用户浏览历史分析而历史记录涉及已删景点可以改成SET_NULL注意字段要允许为空。答辩时如果被问到你能把CASCADE和SET_NULL的取舍讲清楚说明你真的思考过数据完整性分数会很高。顺手说一句Django 2.0之后on_delete是必填参数不写会报错所以每个外键字段都要主动去写而不是靠默认值蒙混。4.3 图片上传与MEDIA配置封面图不显示的排查思路又一个高频翻车点景点封面图上传后页面显示不出来。原因只有一个——MEDIA配置不对。需要检查这几个地方settings.py里配置MEDIA_URL /media/和MEDIA_ROOT BASE_DIR / mediaurls.py里在主URL配置中加上if settings.DEBUG: urlpatterns static(settings.MEDIA_URL, document_rootsettings.MEDIA_ROOT)。这两个配置缺一不可。另外注意如果部署时用的是sorl-thumbnail或别的什么缩略图库图片显示逻辑更复杂建议毕业设计阶段不做缩略图直接用Django原生的图片URL。图片存储也用ImageField(upload_tospots/)这个字段会自动把上传文件放进MEDIA_ROOT/spots/, 数据库存的路径是/media/spots/xxx.jpg模板里直接设置img src{{ spot.image.url }}Django会自动拼出完整URL。4.4 静态文件路径问题static加载不出来的真实原因静态文件CSS、JS、图片加载不出来的问题网上问的人最多主要体现在两种场景一种是模板里写了{% load static %}却还是404另一种是本地好好的换了个环境就坏了。大部分原因是模板中直接用link hrefcss/style.css写死了相对路径而Django的静态文件必须通过{% static css/style.css %}模板标签来渲染绝对URL。少数情况是settings.py里忘了设置STATICFILES_DIRS或者DEBUG False后没有再处理静态文件收集python manage.py collectstatic。毕业设计阶段建议全程保持DEBUG True静态文件问题基本不会出现。如果以后想部署上线再专门研究静态文件收集与服务器配置不必在毕设阶段钻这个牛角尖。4.5 后台管理界面美化用现成库快速出效果自带Admin后台很实用但长得太素了答辩演示管理功能时观感不佳。强烈建议用现成的后台美化库。热门选择有两个django-simpleui和django-unfold。直接说结论如果你追求国内毕设最稳妥、文档最全的方案用django-simpleui中文界面、开箱即用、风格是常见的侧边栏后台管理样式支持主题切换基本不用改任何模板就能显示成高级后台的样子。如果想要更现代、更新颖的UI风格django-unfold最近很火视觉上更接近现代SaaS后台设计侧边栏和卡片布局更精致但文档相对少一点适合有探索精神的同学。# settings.py 中 INSTALLED_APPS 的配置示例 INSTALLED_APPS [ unfold, # 放在 django.contrib.admin 之前 django.contrib.admin, ... ]不管用哪个核心都是为了提升演示观感。后台管理不要花过多时间定制太复杂的功能本质上后台是给管理员用的展示出能增删改查、能统计数据、页面整齐美观就够了。5. 开发排期、源码整理与答辩要点8周时间分配实测很多学生在毕设上的时间规划是前松后紧前期查资料两周最后熬夜一周赶代码。这种模式做出来的系统质量很难保障答辩容易露怯。我建议按8周时间安排任务分阶段推进节奏稳每周目标明确。5.1 8周排期参考周次主要任务产出物第1周需求分析写完整的功能清单设计数据库表结构需求文档 数据库ER图第2周搭建Django项目配置虚拟环境、数据库创建核心app可运行的空项目/首页第3周实现用户认证注册/登录/权限控制完成用户中心注册登录模块第4周实现景点模块列表页、详情页、搜索筛选景点浏览/搜索功能第5周实现收藏、评分、评论模块用户互动功能第6周实现推荐模块热度推荐、基于内容推荐、协同过滤可选推荐系统核心第7周后台管理完善、编写模拟数据脚本、功能测试与修bug完整可演示系统第8周写论文初稿、整理源码、准备答辩PPT论文 答辩材料注意第8周千万不要拿来开发新功能时间必须给到论文和答辩准备。一篇论文哪怕代码再好文字表达乱糟糟也容易影响评分。如果代码做得快论文时间充裕如果代码拖到第7周末才跑通论文仓促赶出来质量很难达到预期。5.2 源码整理的几件小事源码是毕业设计的门面整理不规范会显得整体粗糙。这里说几个常被无视的小事目录结构必须清晰。项目根目录下至少要有apps或按功能拆分的django app目录、templates、static、media、requirements.txt、README.md。不要把所有的模板和静态文件杂乱堆在一个地方更不要把venv环境目录传进代码包。requirements.txt要可复现。生成方式很简单pip freeze requirements.txt。这样答辩老师想看代码时新建一个虚拟环境就能跑起来。千万别在文档里手写依赖容易漏。README.md要写得像样。包含这几段项目简介、功能列表、运行环境要求Python版本、Django版本、启动步骤创建venv、安装依赖、数据库迁移、创建超级用户、填充模拟数据、测试账号说明。这不仅是给别人看的也是在答辩现场快速恢复演示环境的操作清单。注释和命名要规范。视图函数用list_view、detail_view、recommend_view这样自解释的名字模型类用ScenicSpot、Category这样的单数命名不要用myspots这种随意的名字。给推荐算法核心函数写10到20行注释说明输入输出和算法思路。答辩时翻代码给老师看规范程度一目了然。5.3 答辩高频问题与准备思路最后说说答辩整个项目的展示效果一半取决于代码一半取决于你怎么讲。我把带学生时出现率最高的问题整理成一张表你提前准备怎么答。高频问题高分回答思路为什么选Django做这个项目强调Django的MTV架构清晰、自带ORM和Admin便于快速开发、内置用户认证系统安全可靠适合中小型Web应用推荐算法你是怎么实现的按三层讲热度加权排序公式、基于内容的用户画像匹配类别统计、协作过滤余弦相似度加自适应降级重点讲数据如何驱动算法数据库表为什么这样设计强调外键关系、数据完整性on_delete选择、减少冗余评论与评分合表、支撑推荐功能浏览记录表系统有什么安全性考虑密码加密Django默认PBKDF2、用户登录权限控制、防止SQL注入ORM参数化查询、XSS基本防护模板转义如果数据量很大你的系统怎么办诚实指出当前是学习和演示阶段数据量不大但补充说明select_related优化、分页展示、未来可加缓存Redis和搜索引擎Elasticsearch的思路回答技巧就一条先给结论再解释理由最后补一个如果重新做我会怎么提升。比如推荐算法的问题回答顺序是我用了基于内容推荐 协同过滤 热度加权三套机制其中基于内容推荐是主路径……如果重新做我会尝试用Word2Vec对景点描述做向量化进一步提升相似度匹配的准确率。这样的回答既体现了完成度又展示了延伸思考空间。我个人带项目的体会是毕业设计本质上是一次完整的工程实践训练别指望做出行业级产品但要做成一个你每个细节都能讲清楚的成品。旅游景点推荐系统这个题目正好压在那个跳一跳够得着的点上——Django不玄乎推荐算法不深不见底数据不依赖外部接口所有复杂度都在可控范围内。只要按上面的路径一步步走每周都有成果产出答辩时你手里的每一个模块、每一行关键代码都能讲出设计理由和踩坑过程这份从容就是毕设高分最好的保障。最后再分享一个实用小技巧把写好的推荐算法核心函数单独抽成一个recommend_service.py文件尽量保持不依赖Django的HTTP层只接收用户ID返回推荐结果列表。这样你在论文算法设计章节可以直接贴出这个文件的核心逻辑答辩时也能脱开页面单独演示推荐结果的变化——比如换一个用户ID、多一条浏览记录推荐列表立刻跟着变。把这个实时变化的过程在答辩现场跑一遍效果比放十页PPT都好。
返回列表