当前位置: 首页 > 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.xdnf.cn/news/1431451.html

相关文章:

  • 多个大体积PDF文件怎么按数量批量拆分成多个单独文件
  • sed相关知识
  • 国行 iPhone17 会支持 eSIM 吗?最新爆料与区别解读
  • 华晨宇火星演唱会苏州站连唱三晚 万人狂欢共度浪漫七夕
  • 便携式显示器怎么选?:6大关键指标全解析
  • Windows 命令行:父目录与子目录
  • 科研绘图(二):R 语言实现小鼠脑图谱 3D 渲染,附完整代码与数据获取指南
  • 【Datawhale之Happy-LLM】3种常见的decoder-only模型——Github最火大模型原理与实践教程task07
  • C++的演化历史
  • C语言精选100道编程题(附有图解和源码)
  • B2B营销面临的一些主要问题
  • PyTorch实战——GoogLeNet与Inception详解
  • 【AI - nlp】Transformer输入部分要点
  • 无人机小尺寸RFSOC ZU47DR板卡
  • 无人机GPS悬停模块技术解析
  • Swift 解法详解:LeetCode 369《给单链表加一》
  • HTML应用指南:利用POST请求获取全国便利蜂门店位置信息
  • PyTorch 面试题及详细答案120题(106-115)-- 理论与拓展
  • Docker零基础入门指南
  • 两台电脑通过网线直连共享数据,设置正确,却互相ping不通的解决方法
  • 【设计模式】通俗讲解设计模式的七大原则
  • Linux——简单线程池封装案例
  • Sping Web MVC入门
  • 【机器学习深度学习】向量检索到重排序:RAG 系统中的优化实践
  • 关于ANDROUD APPIUM安装细则
  • 分页功能设计
  • MYSQL配置复制拓扑知识点
  • 【54页PPT】数字化转型数据中台解决方案(附下载方式)
  • spring boot 整合AI教程
  • 解析ELK(filebeat+logstash+elasticsearch+kibana)日志系统原理以及k8s集群日志采集过程