1. 项目概述:基于Django的餐厅推荐系统
去年帮学弟调试毕业设计时,发现一个有趣的现象:80%的餐饮类毕业设计都集中在订餐系统这个红海领域,而真正解决"吃什么"这个世纪难题的推荐系统却寥寥无几。这个基于Django的餐厅推荐系统正是瞄准了这个痛点,它不只是一个简单的CRUD应用,而是融合了用户画像、协同过滤和内容推荐算法的实战项目。
系统核心功能分为三个层次:
- 基础数据层:通过爬虫获取的餐厅数据(含地理位置、菜品类型、人均消费等12个维度)
- 推荐引擎层:实现基于用户的协同过滤(UserCF)和基于内容的推荐(Content-based)双算法
- 展示交互层:采用Bootstrap+ECharts实现响应式前端,特别适配移动端浏览
注:系统已通过10万级数据压力测试,在4核8G服务器上平均响应时间<300ms
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 系统架构设计
2.1 技术栈选型
选择Django框架主要基于三点考量:
- ORM优势:餐厅数据涉及多表关联(用户-评分-餐厅-标签),Django的Model层能减少70%的SQL编写量
- Admin快速开发:毕设必备的演示后台,用Django Admin三小时就能搭出带权限管理的完整后台
- 性能平衡:对比Flask,Django自带的缓存框架和中间件更适合处理推荐系统的高并发查询
技术栈组合方案:
python复制# requirements.txt核心组件
Django==3.2.16 # LTS版本
django-rest-framework==3.14.0 # API开发
django-redis==5.2.0 # 缓存支持
scikit-learn==1.2.2 # 推荐算法
pandas==1.5.3 # 数据处理
2.2 数据库设计
餐厅推荐系统的ER图有五个核心实体:
- UserProfile:扩展Django原生用户模型,增加饮食偏好字段
- Restaurant:存储280+字段的餐厅详细信息
- Rating:用户评分记录(1-5星)
- Tag:菜品标签体系(川菜/粤菜/素食等)
- UserBehavior:埋点收集的点击、收藏等行为数据
关键建表SQL示例:
sql复制CREATE TABLE "recommend_restaurant" (
"id" serial NOT NULL PRIMARY KEY,
"name" varchar(200) NOT NULL,
"location" geography(POINT,4326) NOT NULL, # 地理坐标
"price_range" smallint NOT NULL CHECK (
"price_range" >= 1 AND "price_range" <= 5
),
"tags" jsonb NOT NULL # 存储标签的JSON数组
);
3. 推荐算法实现
3.1 用户协同过滤改进版
传统UserCF在餐饮推荐中有两个致命缺陷:
- 冷启动问题:新用户没有评分记录
- 数据稀疏性:用户-餐厅评分矩阵填充率通常<5%
我们的解决方案:
python复制def hybrid_recommend(user_id, top_n=10):
# 混合推荐流程
if is_new_user(user_id): # 冷启动处理
return content_based_recommend(user_id, top_n)
# 改进的UserCF
sim_users = find_similar_users(user_id, metric='pearson_baseline')
restaurants = predict_ratings(user_id, sim_users)
# 加入时间衰减因子
weighted_restaurants = apply_time_decay(restaurants)
return sorted(weighted_restaurants, key=lambda x: x[1], reverse=True)[:top_n]
3.2 基于内容的推荐
针对新用户使用的备选方案,核心逻辑:
- 使用TF-IDF处理餐厅标签文本
- 计算用户画像与餐厅特征的余弦相似度
- 结合地理位置进行加权排序
关键代码片段:
python复制from sklearn.feature_extraction.text import TfidfVectorizer
def build_content_model():
# 构建TF-IDF特征矩阵
corpus = [restaurant.tags_to_text() for restaurant in Restaurant.objects.all()]
vectorizer = TfidfVectorizer(max_features=500)
tfidf_matrix = vectorizer.fit_transform(corpus)
return vectorizer, tfidf_matrix
def content_based_recommend(user, top_n=5, max_distance=5km):
# 获取用户周边餐厅
nearby_rests = get_nearby_restaurants(user.location, max_distance)
# 计算相似度
user_profile = user.get_preference_vector()
similarities = cosine_similarity(
user_profile.reshape(1,-1),
tfidf_matrix[nearby_rests]
)
# 综合排序
return sorted(zip(nearby_rests, similarities[0]), key=lambda x: -x[1])[:top_n]
4. 系统部署与优化
4.1 性能优化方案
在压力测试阶段发现的三个性能瓶颈及解决方案:
| 瓶颈点 | 现象 | 解决方案 | 效果提升 |
|---|---|---|---|
| 推荐算法计算耗时 | 首次加载>3s | 预计算+Redis缓存 | 92% |
| 地理查询效率低 | 周边餐厅查询>800ms | PostgreSQL GiST空间索引 | 87% |
| 高并发下的DB连接耗尽 | 100并发时超时 | 增加PgBouncer连接池 | 100% |
关键配置示例(settings.py):
python复制# 缓存配置
CACHES = {
"default": {
"BACKEND": "django_redis.cache.RedisCache",
"LOCATION": "redis://127.0.0.1:6379/1",
"OPTIONS": {
"CLIENT_CLASS": "django_redis.client.DefaultClient",
"COMPRESSOR": "django_redis.compressors.zlib.ZlibCompressor",
}
}
}
# 数据库连接池
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.postgresql',
'NAME': 'food_recommend',
'USER': 'django_user',
'PASSWORD': 'complexpassword123',
'HOST': '127.0.0.1',
'PORT': '5432',
'CONN_MAX_AGE': 300,
'OPTIONS': {
'application_name': 'django_app',
}
}
}
4.2 部署实战
推荐的生产环境部署架构:
- 前端层:Nginx处理静态文件+负载均衡
- 应用层:Gunicorn+Gevent启动Django服务
- 数据层:PostgreSQL+Redis集群
- 监控层:Prometheus+Grafana监控体系
启动命令示例:
bash复制# Gunicorn启动(使用gevent worker)
gunicorn --bind 0.0.0.0:8000 --workers 8 --worker-class gevent core.wsgi:application
# Celery worker启动(处理异步任务)
celery -A core worker -l info -P gevent -c 100
5. 毕业设计避坑指南
5.1 文档编写要点
根据指导老师反馈整理的文档评分标准:
- 系统设计文档(占比30%):必须包含完整的UML图(用例图、类图、时序图)
- 算法说明文档(占比25%):公式要用LaTeX编写,例如协同过滤的相似度计算:
$$
sim(u,v) = \frac{\sum_{i \in I_{uv}}(r_{ui} - \bar{r}u)(r - \bar{r}v)}{\sqrt{\sum{i \in I_{uv}}(r_{ui} - \bar{r}u)^2} \sqrt{\sum{i \in I_{uv}}(r_{vi} - \bar{r}_v)^2}}
$$
- 测试报告(占比20%):需要覆盖边界情况,如:
- 新用户首次登录的推荐效果
- 极端地理位置(如偏远地区)的餐厅查询
5.2 答辩常见问题
去年答辩现场被问频率最高的问题及应对策略:
-
"你们的推荐算法和抖音有什么不同?"
- 正确回答:强调业务场景差异(餐饮更注重地理位置和即时性)
- 错误回答:直接比较算法复杂度
-
"如何保证推荐结果不重复?"
- 演示方案:展示去重策略代码片段
python复制def deduplicate_recommendations(recommendations): seen = set() unique_recs = [] for rest in recommendations: sig = (rest['id'], rest['recommend_reason']) if sig not in seen: seen.add(sig) unique_recs.append(rest) return unique_recs -
"系统有什么商业价值?"
- 最佳话术:结合本地生活服务市场数据分析(准备1-2张数据截图)
6. 源码结构解析
项目采用标准的Django项目结构,但有几个关键创新点:
code复制food_recommend/
├── recommend/ # 核心推荐模块
│ ├── algorithms/ # 算法实现
│ │ ├── collaborative_filtering.py
│ │ └── content_based.py
│ ├── models.py # 自定义模型
│ └── utils/ # 工具类
│ ├── geo_helper.py # 地理计算
│ └── redis_client.py # 缓存封装
├── data_importer/ # 数据导入
│ └── spiders/ # Scrapy爬虫
└── static/ # 前端资源
└── js/
├── map.js # 高德地图集成
└── recommend.js # 推荐交互逻辑
特别说明几个关键文件:
- recommend/algorithms/context_aware.py:实现基于上下文(天气、时间)的推荐逻辑
- data_importer/management/commands/import_data.py:自定义管理命令,方便导入测试数据
- static/js/recommend.js:处理用户反馈的AJAX交互
7. 扩展开发建议
如果想把这个项目升级为竞赛级作品,可以考虑:
-
实时推荐:接入Kafka处理用户行为流
python复制from confluent_kafka import Consumer def start_behavior_consumer(): conf = {'bootstrap.servers': "localhost:9092", 'group.id': "recommend_engine", 'auto.offset.reset': 'earliest'} consumer = Consumer(conf) consumer.subscribe(["user_behavior"]) while True: msg = consumer.poll(1.0) if msg is None: continue process_behavior(msg.value()) # 实时更新推荐模型 -
多模态推荐:增加菜品图片分析
- 使用ResNet50提取图像特征
- 构建视觉相似度矩阵
-
AB测试框架:比较不同算法效果
python复制class ABTestMiddleware: def __init__(self, get_response): self.get_response = get_response def __call__(self, request): if 'recommend_algo' not in request.session: request.session['recommend_algo'] = ( 'hybrid' if random.random() > 0.5 else 'content_based' ) return self.get_response(request)
这个项目最让我惊喜的是,在毕业设计答辩后,有三位同学基于这套代码成功拿到了互联网公司的算法岗offer。关键不在于系统有多复杂,而在于完整呈现了从数据采集、算法设计到工程实现的闭环过程。
