当前位置: 首页 > news >正文

278-基于Django的协同过滤旅游推荐系统

基于Django的协同过滤旅游推荐系统:从0到1落地实战

一套可跑通、可扩展、可二开的旅游推荐系统,涵盖用户端与管理端,内置 UserCF/ItemCF 推荐,支持评分、收藏、浏览行为采集与“猜你喜欢”。本文完整拆解技术架构、目录结构与核心代码,并预留可视化展示位。

目录

  • 项目概览
  • 技术栈
  • 目录结构
  • 数据模型设计
  • 核心业务流程
    • 首页与分类检索
    • 猜你喜欢(个性化推荐)
    • 协同过滤算法实现
  • 接口与路由
  • 前端与可视化)
  • 部署与运行
  • 性能与扩展建议
  • 常见问题 FAQ
  • 结语与联系方式

项目概览

本项目是一个基于 Django 的智能旅游推荐系统,面向“景点、美食、酒店、路线”等多类型内容,支持:

  • 用户体系:注册、登录、个人信息管理、头像上传
  • 行为采集:评分、收藏、浏览记录
  • 推荐能力:基于用户/物品的协同过滤(UserCF/ItemCF),“猜你喜欢”与随机补全
  • 内容展示:首页轮播、卡片列表、详情页、搜索与类型切换
  • 管理后台:基于 Django Admin 的数据增删改查

技术栈

  • 后端:Django 3.x,Django Admin
  • 数据库:MySQL
  • 前端:Django Template、Bootstrap、jQuery
  • 可视化:ECharts(本地静态资源)
  • 推荐算法:UserCF、ItemCF(余弦相似度、邻域、预测评分)
  • 其他:Pillow、SimpleUI(美化后台)

目录结构

TravelRecSystem/
├── apps/
│   ├── app/               # 核心业务模型:美食(App)、酒店(Hotel)、景点(ScenicSpot)、路线(Router)
│   ├── user/              # 用户模块
│   ├── record/            # 评分记录
│   ├── collection/        # 收藏记录
│   ├── comment/           # 评论(如需)
│   ├── type/              # 类型管理
│   ├── index/             # 首页、推荐、检索
│   └── common/            # 常量、历史记录、通用视图
│       └── ...
├── apps/util/cfra/        # 协同过滤算法实现(UserCF/ItemCF 等)
├── static/                # 静态资源(CSS/JS/Images)
├── media/                 # 媒体资源(上传头像/图片)
├── templates/             # 模板页面(首页、详情、用户中心等)
├── TravelRecSys/          # Django 项目配置、全局路由
├── requirements.txt
├── design_278_travel.sql  # 初始数据库结构
└── manage.py

数据模型设计

apps/app/models.py 为例,项目内置了多类型旅游对象:

class App(models.Model):name = models.CharField(max_length=100)typeid = models.ForeignKey('type.Type', models.CASCADE, db_column='typeid')image = models.ImageField(upload_to='')deal = models.CharField(blank=True, max_length=1000)price = models.CharField(blank=True, max_length=50)address = models.TextField(max_length=200)grade = models.CharField(blank=True, max_length=5)comment_count = models.IntegerField(blank=True)meishi_url = models.CharField(blank=True, max_length=300)img_url = models.CharField(blank=True, max_length=300)class Hotel(models.Model):name = models.CharField(max_length=255)hotelHeadPicture = models.CharField(max_length=255)commentScore = models.FloatField()city = models.CharField(max_length=255)description = models.TextField()class ScenicSpot(models.Model):name = models.CharField(max_length=255)img = models.CharField(max_length=255)ticket = models.IntegerField()star_num = models.IntegerField()  # 收藏人数lat = models.FloatField()lng = models.FloatField()class Router(models.Model):name = models.CharField(max_length=255)img = models.CharField(max_length=255)detail = models.TextField()

用户行为数据(评分、收藏、浏览)在推荐中至关重要:

# apps/record/models.py
class Record(models.Model):score = models.IntegerField(validators=[MaxValueValidator(5), MinValueValidator(1)])userid = models.ForeignKey('user.User', models.CASCADE, db_column='userid')appid = models.IntegerField()  # 评分对象IDtype = models.CharField(max_length=255)createtime = models.DateTimeField(auto_now_add=True)# apps/collection/models.py
class Collection(models.Model):userid = models.ForeignKey('user.User', models.CASCADE, db_column='userid')appid = models.IntegerField()  # 收藏对象IDtype = models.CharField(max_length=255)createtime = models.DateTimeField(auto_now_add=True)# apps/common/models.py
class History(models.Model):userid = models.ForeignKey('user.User', models.CASCADE, db_column='userid')appid = models.IntegerField()  # 浏览对象IDtype = models.CharField(max_length=255)createtime = models.DateTimeField(auto_now_add=True)

核心业务流程

首页与分类检索

首页聚合轮播与卡片位,支持类型切换和搜索,代码见 apps/index/views.py

def index(request):# 未传 type 时渲染首页轮播与卡片if not request.GET.get('type'):carousel_items = []top_app = App.objects.order_by('?').first()top_hotel = Hotel.objects.order_by('?').first()top_scenic = ScenicSpot.objects.order_by('?').first()top_router = Router.objects.order_by('?').first()# ... 组装 carousel_items 与四类卡片数据 apps/hotels/scenics/routersreturn render(request, 'index/home.html', context)# 传入 type 与 keyword 时,进入列表检索与分页逻辑item_type = request.GET.get('type')keyword = request.GET.get('keyword', '')page = int(request.GET.get('page', 1))page_size = 10if item_type == 'scenic_spot':queryset = ScenicSpot.objects.filter(Q(name__icontains=keyword)) if keyword else ScenicSpot.objects.all()queryset = queryset.annotate(is_gif=Case(When(img__iendswith='.gif', then=Value(1)), default=Value(0), output_field=IntegerField())).order_by('is_gif', '-star_num')paginator = Paginator(queryset, page_size)items = paginator.get_page(page)# hotel/app/router 分支类似return render(request, 'index/index.html', context=data)

亮点:

  • 首页轮播从“美食/酒店/景点/路线”四类随机抽取,过滤 GIF
  • 列表按类型与关键词检索,并对景点优先展示“非 GIF + 收藏热度高”的内容

猜你喜欢(个性化推荐)

登录用户可见,综合评分、收藏、浏览构建评分矩阵,调用 UserCF 生成推荐:

def guess_you_like(request):if not request.session.get(Constant.session_user_isLogin, None):return render(request, 'index/guess.html', {"not_login": True})cUserid = request.session.get(Constant.session_user_id)records = Record.objects.all()collections = Collection.objects.all()historys = History.objects.all()dataModel = setDataModelWithRules(historys, records, collections, None)userCf = UserCF()scenic_list = list(getRecommendItems(userCf.recommend(dataModel, int(cUserid)), 'scenic_spot') or [])# 若不足 6 条,随机补全,hotel/app/router 同理return render(request, 'index/guess.html', context)

评分矩阵构建规则(关键加权策略):

def setDataModelWithRules(historys, records, collections, item_type):dataModel = DataModel()# 评分:1→-2、2→-1、3→+1、4→+2、5→+3(最低不低于0)for record in records:score = float(record.score)if score == 1: adjusted_score = max(0, score - 2)elif score == 2: adjusted_score = max(0, score - 1)elif score == 3: adjusted_score = score + 1elif score == 4: adjusted_score = score + 2elif score == 5: adjusted_score = score + 3else: adjusted_score = scoredataModel.setUserItemValue(record.userid_id, record.appid, adjusted_score)dataModel.setItemUserValue(record.appid, record.userid_id, adjusted_score)# 收藏:+5 分(在已有分数基础上叠加)for collection in collections:current_score = dataModel.userItemPrefMatrixDic.get(collection.userid_id, {}).get(collection.appid, 0)new_score = current_score + 5 if current_score else 5dataModel.setUserItemValue(collection.userid_id, collection.appid, new_score)dataModel.setItemUserValue(collection.appid, collection.userid_id, new_score)# 浏览:+1 分(在已有分数基础上叠加)for history in historys:current_score = dataModel.userItemPrefMatrixDic.get(history.userid_id, {}).get(history.appid, 0)new_score = current_score + 1 if current_score else 1dataModel.setUserItemValue(history.userid_id, history.appid, new_score)dataModel.setItemUserValue(history.appid, history.userid_id, new_score)return dataModel

协同过滤算法实现

UserCF 通过“用户-用户相似度 + 邻域 + 预测评分”进行推荐:

class UserCF(object):def recommend(self, dataModel, cUserid):# 1) 基于余弦相似度计算目标用户与其他用户相似度userSimilarityDic = UserSimilarity().getUserSimilaritys(cUserid, CosineSimilarity(), dataModel)# 2) 选取前 K 个最近邻kNUserNeighborhood = UserNeighborhood().getKUserNeighborhoods(userSimilarityDic)# 3) 预测评分并返回 TopN 推荐recommenderItemFinalDic = UserRecommender().getUserRecommender(cUserid, dict(kNUserNeighborhood), dataModel)return sorted(recommenderItemFinalDic.items(), key=operator.itemgetter(1), reverse=True)[:Constant.cfCount]

ItemCF 通过“物品-物品相似度 + 用户历史偏好”进行推荐:

class ItemCF(object):def recommend(self, dataModel: DataModel, cUserid):# 1) 计算物品-物品相似度itemSimilarityDic = ItemSimilarity().getItemSimilaritys(cUserid, CosineSimilarity(), dataModel)# 2) 基于用户的已评物品与候选物品的相似度,预测喜好recItemDic = ItemRecommender().getItemRecommender(cUserid, itemSimilarityDic, dataModel)return sorted(recItemDic.items(), key=operator.itemgetter(1), reverse=True)[:Constant.cfCount]

接口与路由

主路由 TravelRecSys/urls.py

urlpatterns = [path('', include('apps.common.urls')),path('', include('apps.index.urls')),path('index/', include('apps.index.urls')),path('app/', include('apps.app.urls')),path('user/', include('apps.user.urls')),path('record/', include('apps.record.urls')),path('collection/', include('apps.collection.urls')),path('comment/', include('apps.comment.urls')),path('admin/', admin.site.urls),
] + static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)

首页与推荐 apps/index/urls.py

urlpatterns = [path('', views.index),         # 首页/列表path('guess', views.guess_you_like),  # 猜你喜欢(登录用户)
]

前端与可视化

🦀 项目源码获取,码界筑梦坊各平台同名,博客底部含联系方式卡片,欢迎咨询!
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

页面模板位于 templates/,静态资源位于 static/。建议在首页与“猜你喜欢”侧添加 ECharts 大盘,可展示:

  • 省份分布(Geo/Map)
  • 类型占比(Pie/Donut)
  • 收藏与评分趋势(Line/Bar)

ECharts 选项(示例,可放入 templates/index/kanban.html):

<div id="chart1" style="height: 360px;"></div>
<script src="/static/js/echarts.min.js"></script>
<script>var chart = echarts.init(document.getElementById('chart1'));var option = {title: { text: '景点类型占比' },tooltip: { trigger: 'item' },legend: { bottom: 0 },series: [{type: 'pie', radius: ['40%', '70%'],data: [{ name: '景点', value: 123 },{ name: '美食', value: 98 },{ name: '酒店', value: 76 },{ name: '路线', value: 45 }]}]};chart.setOption(option);
</script>

部署与运行

  1. 安装依赖
pip install -r requirements.txt
  1. 配置 MySQL 连接(在 TravelRecSys/settings.py 内修改数据库配置)

  2. 数据迁移与初始化

python manage.py makemigrations
python manage.py migrate
  1. 创建超级用户
python manage.py createsuperuser
  1. 启动服务
python manage.py runserver
  1. 访问
  • 前台:http://localhost:8000/
  • 后台:http://localhost:8000/admin/

性能与扩展建议

  • 分页与延迟加载:列表页默认分页 10 条,减少单页渲染压力
  • 缓存:热门资源与推荐结果可加缓存(如基于用户维度的短期缓存)
  • 向量化召回:可引入向量检索(Faiss/ScaNN)作为召回层,CF 作为精排
  • 多信号融合:评分、收藏、浏览之外,可引入评论情感、停留时长等行为信号
  • 推荐评估:A/B 测试、点击率/转化率监控,闭环优化

常见问题 FAQ

  1. ECharts 图表不显示?
    • 确认 static/js/echarts.min.js 是否就绪,并正确引入
  2. 图片 404 或显示异常?
    • 检查 MEDIA_URL/MEDIA_ROOT 配置,确保图片位于 media/ 目录
  3. 后台未显示模型?
    • 使用超级用户登录 /admin/,确认相关模型在各 app 的 admin.py 注册
  4. 推荐结果为空?
    • 初期行为数据不足时,会进行随机补全;建议先产生评分/收藏/浏览数据
  5. 数据库连接失败?
    • 检查 MySQL 服务、账号权限与 settings.py 配置

结语与联系方式

如果你正在做课程设计、毕设或企业内小型 PoC,本项目是不错的起点:结构清晰、功能完整、算法可替换。欢迎在此基础上二次开发,如引入多模态、图网络或大模型增强。

联系方式:码界筑梦坊各大平台同名

s.min.js` 是否就绪,并正确引入
2. 图片 404 或显示异常?

  • 检查 MEDIA_URL/MEDIA_ROOT 配置,确保图片位于 media/ 目录
  1. 后台未显示模型?
    • 使用超级用户登录 /admin/,确认相关模型在各 app 的 admin.py 注册
  2. 推荐结果为空?
    • 初期行为数据不足时,会进行随机补全;建议先产生评分/收藏/浏览数据
  3. 数据库连接失败?
    • 检查 MySQL 服务、账号权限与 settings.py 配置

结语与联系方式

如果你正在做课程设计、毕设或企业内小型 PoC,本项目是不错的起点:结构清晰、功能完整、算法可替换。欢迎在此基础上二次开发,如引入多模态、图网络或大模型增强。

联系方式:码界筑梦坊各大平台同名

http://www.dtcms.com/a/362602.html

相关文章:

  • 详解Grafana k6 的阈值(Thresholds)
  • os.path:平台独立的文件名管理
  • sql执行过程
  • Tomcat 全面指南:从目录结构到应用部署与高级配置
  • Java-Spring入门指南(一)Spring简介
  • WPF曲线自定义控件 - CurveHelper
  • 大模型是如何“学会”思考的?——从预训练到推理的全过程揭秘
  • 【完整源码+数据集+部署教程】PHC桩实例分割系统源码和数据集:改进yolo11-Faster-EMA
  • 无需服务器,免费、快捷的一键部署前端 vue React代码--PinMe
  • 搭建分布式Hadoop集群[2025] 实战笔记
  • 【golang长途旅行第36站】golang操作Redis
  • 【自记】Python 中 简化装饰器使用的便捷写法语法糖(Syntactic Sugar)示例
  • ARM汇编记忆
  • 【53页PPT】华为制造行业数字化转型工业互联网智能制造解决方案(附下载方式)
  • MySQL事务+MVCC(精简版,包教包废)
  • 2025华为最值得入的耳机,真的赢麻了!
  • 结构抗震与土木工程研究
  • SylixOS 下的信号系统
  • Vue 3 + TypeScript 现代前端开发最佳实践(2025版指南)
  • Chrome浏览器调用ActiveX控件之allWebOffice在线编辑控件
  • JD潜在前端二面高频题解析
  • mysql5.6+分页时使用 limit+order by 会出现数据重复问题
  • 蓝桥杯算法之基础知识(5)
  • 基于Spark的新冠肺炎疫情实时监控系统_django+spider
  • 数据结构与算法个人学习代码笔记包含leetcode,海贼oj,蓝桥杯,ACM
  • 华为Fit4:腕间助手,守护你的健康,带你开启智慧生活
  • 【字节拥抱开源】 UXO 团队开源 USO: 通过解耦与奖励学习实现的统一风格与主题驱动生成
  • 2025最新“Java 面试八股文 + 各大厂的面试真题”限时开源
  • 美团 LongCat 开源大模型60 亿参数 MoE 架构,赋能开发者加速 AI 应用落地
  • 本地搭建并使用 Redmine 详细教程