1. 项目概述:当美食推荐遇上Django
去年帮学弟调试毕业设计时,发现他用了三天三夜爬取的美食数据,在推荐环节却只做了简单的随机展示。这让我意识到,很多同学在完成Django毕设时,往往把精力都花在了基础CRUD(增删改查)实现上,而忽略了推荐系统这个最能体现项目价值的核心模块。
这个基于Python的美食推荐管理系统,正是为了解决这类痛点而生。它不只是个简单的信息管理平台,而是融合了用户画像分析、协同过滤算法和Django缓存优化的完整解决方案。从技术栈来看,项目前端采用Bootstrap+ECharts实现响应式布局和数据可视化,后端使用Django REST framework构建API接口,推荐模块则整合了基于用户的协同过滤(UserCF)和基于内容的推荐(Content-based)双引擎。
提示:系统默认使用SQLite开发,但我在实际部署时改用MySQL配合Django的ORM迁移,性能提升约40%。建议有条件的同学直接上生产级数据库。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 系统架构设计解析
2.1 技术选型背后的思考
选择Django而非Flask或FastAPI,主要考虑到毕业设计的完整性和可展示性。Django自带的后台管理系统、认证模块和ORM能快速搭建管理功能,让开发者更专注于推荐算法实现。以下是核心组件对比:
| 技术选项 | 选用理由 | 替代方案风险 |
|---|---|---|
| Django 4.2 | 内置Admin可快速开发后台,完善的中间件支持 | Flask需要自行组装各组件 |
| Pandas | 处理用户评分矩阵时向量化操作效率高 | 纯Python实现计算速度慢10倍 |
| Redis | 用户行为缓存和热门菜品排行榜的存储 | 不使用缓存时QPS下降60% |
| ECharts | 毕业答辩时直观展示推荐效果对比 | 原生图表样式简陋 |
2.2 推荐系统工作流
系统采用混合推荐策略,具体流程如下:
- 新用户注册时通过问卷收集基础偏好(辣度接受度、菜品类型等)
- 老用户浏览时实时记录点击/收藏/评分行为
- 每晚定时任务执行:
- 用户相似度矩阵计算(余弦相似度)
- 基于内容的菜品特征提取(TF-IDF处理菜品描述)
- 前端请求推荐时,按7:3比例融合协同过滤和内容推荐结果
python复制# 推荐核心代码片段
def hybrid_recommend(user_id):
cf_rec = user_cf(user_id) # 协同过滤结果
cb_rec = content_based(user_id) # 内容推荐结果
# 混合策略
hybrid = {
item: cf_rec.get(item, 0)*0.7 + cb_rec.get(item,0)*0.3
for item in set(cf_rec) | set(cb_rec)
}
return sorted(hybrid.items(), key=lambda x: -x[1])[:10]
3. 关键实现细节剖析
3.1 用户画像构建
在accounts/models.py中扩展了Django默认用户模型:
python复制class UserProfile(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
spice_tolerance = models.IntegerField( # 辣度耐受指数
choices=[(1,'微辣'),(2,'中辣'),(3,'重辣')],
default=1
)
preferred_cuisines = models.ManyToManyField('CuisineType')
last_active = models.DateTimeField(auto_now=True)
def similarity(self, other):
# 计算用户相似度
cuisine_match = len(set(self.preferred_cuisines.all())
& set(other.preferred_cuisines.all()))
spice_diff = abs(self.spice_tolerance - other.spice_tolerance)
return 0.6*cuisine_match - 0.4*spice_diff
3.2 性能优化实战
在开发过程中发现,当用户量超过1万时,推荐计算耗时从200ms飙升到8s。通过以下优化手段解决:
-
缓存策略:
- 使用Redis缓存用户相似度矩阵(设置24小时过期)
- 热门菜品列表每小时更新一次
python复制# settings.py配置 CACHES = { "recommend": { "BACKEND": "django.core.cache.backends.redis.RedisCache", "LOCATION": "redis://127.0.0.1:6379/1", "TIMEOUT": 86400 # 24小时 } } -
数据库索引优化:
python复制class Rating(models.Model): user = models.ForeignKey(User, on_delete=models.CASCADE, db_index=True) dish = models.ForeignKey('Dish', on_delete=models.CASCADE, db_index=True) score = models.SmallIntegerField() class Meta: indexes = [ models.Index(fields=['user', 'dish'], name='user_dish_idx') ]
4. 典型问题排查实录
4.1 冷启动问题
初期新用户得不到有效推荐,通过以下方案解决:
- 构建菜品特征词表(TF-IDF处理2000+菜品描述)
- 当新用户选择偏好后,立即匹配相似特征菜品
- 混合展示热门榜单和特征匹配结果
4.2 推荐多样性不足
发现川菜爱好者总是收到同类推荐,改进措施:
- 在推荐结果中强制加入10%的随机探索项
- 对过于相似的推荐结果进行去重
python复制def diversify(recommendations, threshold=0.7): diversified = [] for item in recommendations: if not any(similarity(item, exist) > threshold for exist in diversified): diversified.append(item) return diversified
5. 毕业设计增值技巧
5.1 答辩演示技巧
- 在后台预置不同偏好类型的测试账号
- 准备对比实验:关闭推荐算法 vs 开启推荐算法
- 使用ECharts展示推荐前后的点击率提升数据
5.2 文档编写要点
- 在requirements.txt中固定版本号(如Django==4.2.5)
- 数据库迁移脚本单独存放在/migrations/backup
- 接口文档用Postman生成并附带测试用例
我在部署测试时发现,当用户行为数据达到5万条后,SQLite性能急剧下降。这时需要:
- 执行
python manage.py dumpdata > backup.json - 修改settings.py切换MySQL
- 执行
python manage.py migrate --run-syncdb - 通过
python manage.py loaddata backup.json恢复数据
最后分享一个调试技巧:在开发推荐算法时,先用Faker生成1000个虚拟用户的行为数据,可以快速验证算法有效性,比用真实数据测试效率高得多。具体实现可以参考management/commands/generate_testdata.py中的示例代码。
