☰
Python+Django协同过滤商品推荐系统设计与实现
2026/10/17 13:21:27 网站建设 项目流程

1. 项目整体设计与思路拆解

每年到毕业季,我都会接触到不少做推荐系统方向的同学。这个"基于python的协同过滤商品推荐系统",从我经手过的多个相似项目来看,基本可以定位为一个标准的信息管理系统叠加推荐算法模块的组合型课题。说白了,大家想要的不是纯算法研究,而是一个能跑、能演示、能写进论文的系统成品,同时算法部分又不能太薄弱——否则答辩时老师随便问一个"你是怎么算相似度的"就把人问住了。

1.1 核心需求解析:这个课题到底要做什么

先把这个题目拆开看。它的关键词有四个维度:Django(后端框架)、Python(开发语言)、协同过滤(推荐算法)、商品推荐系统(业务场景)。这意味着你要交付的不是一个简单的CRUD管理系统,而是要让人看到"系统里有商品数据能管理"之外,还能看到"用户的行为被记录并被分析、最终产生个性化推荐结果"这三个层次。

以我实际带过的一个模拟项目X为参考,这类系统的典型功能清单大概是这样的:

  • 用户端:注册登录、商品浏览、商品搜索、商品详情、加入购物车、下单、收藏/点赞行为记录、推荐结果页
  • 管理端:商品分类管理、商品管理、用户管理、订单管理、数据统计
  • 算法层:基于用户的协同过滤、基于物品的协同过滤、相似度矩阵计算、Top-N推荐生成
  • 辅助功能:用户行为埋点(浏览记录、评分记录、购买记录)、推荐结果解释(如"因为你看过A,所以推荐B")

一份好的开题报告或任务书,通常就是围绕这几个模块展开的。如果你的题目描述里只提到了"协同过滤商品推荐系统",没有细分功能,那么以上这份清单就是你搭建设计文档的最佳起点。

1.2 技术选型背后的逻辑:为什么是Django而不是别的

在我经手过的毕设项目里,Django出镜率极高,这不是偶然。对大多数做毕设的同学来说,Django有三个无法拒绝的优势。

第一个优势是"全家桶"式的开发体验。ORM(对象关系映射)层帮你去掉了大量写SQL的精力,Admin后台几乎是免费送的——你只需要在admin.py里注册一下数据模型,一个可用的管理后台就出来了,这对商品管理、用户管理这类功能来说简直是量身定做。对于不擅长前端或者不想花太多时间在后端管理页面的同学来说,这个优势极其致命。

第二个优势是模板和静态资源处理的便捷性。商品推荐系统注定要有大量的商品图片、详情页、列表页。Django的模板继承机制配合Bootstrap这类前端框架,可以在非常短的时间内拼出一个看起来还算完整的前端页面。我曾经帮一个同学从零搭过一个商品推荐Demo,总共用了不到两小时就把首页、商品列表、详情页全部跑通了。

第三个优势是Python生态对算法实现的天然支持。协同过滤的核心是计算相似度矩阵、做排序、取Top-N。Python的列表推导式、NumPy的矩阵运算、Pandas的数据处理,都是做这些事情的"本命工具"。你需要做的只是把算法代码写好,然后在Django的视图函数里调用它,把结果传入模板渲染出来。

注意:如果你的项目时间非常紧(比如只剩两周),不要轻易去换Flask或者FastAPI另起炉灶。不是它们不好,而是Django的Admin后台和ORM实在能帮你省下太多时间。FastAPI性能确实好,但毕设答辩看的是完整度和逻辑自洽,技术栈冷门不一定是加分项。

1.3 协同过滤选型的"为什么":基于物品和基于用户的取舍

协同过滤算法本身分为两大类:基于用户(User-Based)和基于物品(Item-Based),还有更进阶的矩阵分解(如SVD)。作为毕设项目,你必须掌握这两个基础算法的区别,还要能说出来"我为什么用这个不用那个"。

基于用户的协同过滤,核心思路是"找相似的人"。如果用户A和用户B历史上对一批商品的评分/购买行为高度一致,那么A买过而B没买过的商品,就有很大概率被推荐给B。这个算法适合用户规模相对较小、用户群体特征明显的场景,比如早期的社交网络推荐。

基于物品的协同过滤,核心思路是"找相似的物品"。它的出发点是:如果大量用户在买了商品X之后又买了商品Y,那么X和Y就存在关联关系。比如你在某电商平台看中了一款球鞋,页面底部会出现"买过这款的用户还看过"——背后很可能就是Item-Based协同过滤在起作用。

在我接触的毕设项目中,我一般建议主推基于物品的协同过滤。原因有两个:

第一,商品数量通常比用户数量更稳定。电商平台每天可能有海量新订单,但商品的种类在一定周期内波动不会太大。物品相似度矩阵可以离线计算、定时更新,系统压力小,这在写系统设计章节时非常加分。

第二,推荐结果更容易解释。基于物品的协同过滤在给用户推商品时,可以带一句"因为您浏览过/购买过某商品,所以推荐你相关商品"。这种解释在论文里、在答辩演示里、在系统截图里都显得非常有理有据。

当然,这不代表基于用户的算法不需要实现。市面上很多完整的毕设源码会把两种方法都实现出来,然后放在同一个推荐服务里做对比测试。这样你在论文里可以多写一个"实验对比"章节,工作量既充实,答辩时谈资也多。

2. 核心功能模块与数据模型设计

这个环节做得好不好,直接决定你后续编码是否顺畅。我不建议一上来就写代码,先把数据库表设计清楚,Django的模型写起来就是"填空"。

2.1 用户行为数据怎么建模:Rating是灵魂

很多人对推荐系统的数据库设计有一个误区,以为有用户表、商品表、订单表就差不多了。这个想法相当危险——因为没有核心的"行为数据表",你的协同过滤算法就是无水之源、无米之炊。

推荐系统的核心资产不是商品信息,而是用户行为。所以你必须设计一张能够承载"用户对商品的反馈"的表,我习惯叫它UserRating表或者UserBehavior表。这张表至少要包含以下字段:

字段名类型说明
id自增主键主键
user_id外键关联用户表
item_id外键关联商品表
rating浮点型用户对商品的评分(如1-5分)
behavior_type整型或字符型行为类型:浏览/收藏/加购/购买
create_time时间戳行为发生时间

为什么评分和行为类型要放在同一张表里?这是一个实操中总结出来的经验。如果用户只是浏览了商品但没有评分,rating可以默认为空或者0;如果用户明确打了分,那rating就有值。这样一张表既承载了显式反馈(评分),又承载了隐式反馈(浏览/购买行为),在做算法时灵活性非常大。比如基于物品的协同过滤,一般来说只用购买行为就够了,但如果你需要应对用户没有购买记录的情况,浏览行为的权重同样能派上用场。

商品表的设计也要稍加用心。除了常规的id、名称、类别、价格、库存外,我建议额外加一行"销量"或者"热度"字段。原因很简单——新建商品没有任何行为数据,协同过滤拿它完全没办法,这时候就需要靠热度值做冷启动兜底。

2.2 用户端与后台管理的功能闭环

把数据模型理清之后,功能页面就顺理成章了。我按实际的开发优先级给出一份排序,这样你安排时间时心里有底。

第一优先级:用户注册登录、商品列表、商品详情、购物车、订单创建。这一套组合拳打下来,你的系统已经从"空壳"变成了"一个能用的商城"。

第二优先级:评分/收藏/加入购物车这些行为操作的埋点。注意,这里不只是"能操作",更重要的是"每次操作都要写入UserRating表"。我见过不少同学,系统的商城功能做得花里胡哨,结果后台的实验数据一塌糊涂——因为用户的所有行为根本没有被记录。做推荐系统的Demo,行为数据记录才是命根子。

第三优先级:管理后台的商品管理、用户管理、订单管理和行为数据查看。Django Admin自带CRUD,你只需要把数据模型注册进去,后台就有了。但建议你稍微定制一下Admin——比如给商品列表加图片预览、给订单列表增加状态筛选,操作很简单,但答辩演示时观感会好很多。

第四优先级:推荐结果页和推荐解释。在用户中心的显著位置放一个"为你推荐"的区块,调用算法模块生成商品列表,每条推荐带上推荐理由。这一点哪怕做得粗糙,效果也比没有强十倍。

我常跟同学说的一句话是:评分系统和行为记录这两块是推荐系统的"心脏"。做代码走查时,老师大概率会先看你的数据表格,再看算法代码。数据表设计得凌乱、行为没有记录,算法代码写得再好,都会显得根基不稳。

2.3 数据模型中的关键代码实现参考

既然说到Django,我顺手给一份最基础的数据模型参考代码,它几乎可以直接复用到你的项目里。

from django.db import models from django.contrib.auth.models import User class Category(models.Model): name = models.CharField(max_length=64, verbose_name='分类名称') parent = models.ForeignKey( 'self', null=True, blank=True, on_delete=models.CASCADE, verbose_name='父级分类' ) class Meta: verbose_name = '商品分类' verbose_name_plural = verbose_name def __str__(self): return self.name class Item(models.Model): name = models.CharField(max_length=128, verbose_name='商品名称') category = models.ForeignKey( Category, on_delete=models.CASCADE, verbose_name='所属分类' ) price = models.DecimalField(max_digits=10, decimal_places=2, verbose_name='价格') stock = models.IntegerField(default=0, verbose_name='库存') sales = models.IntegerField(default=0, verbose_name='销量热度') cover_image = models.ImageField( upload_to='item_cover/%Y/%m/%d', blank=True, null=True, verbose_name='封面图' ) description = models.TextField(blank=True, verbose_name='商品描述') created_at = models.DateTimeField(auto_now_add=True, verbose_name='上架时间') class Meta: verbose_name = '商品信息' verbose_name_plural = verbose_name def __str__(self): return self.name class UserRating(models.Model): """ 用户行为记录表,同时承载显式评分与隐式行为。 behavior_type: 1浏览 2收藏 3加购 4购买 5评分 """ BEHAVIOR_CHOICES = ( (1, '浏览'), (2, '收藏'), (3, '加购'), (4, '购买'), (5, '评分'), ) user = models.ForeignKey(User, on_delete=models.CASCADE, verbose_name='用户') item = models.ForeignKey(Item, on_delete=models.CASCADE, verbose_name='商品') rating = models.FloatField(null=True, blank=True, verbose_name='评分') behavior_type = models.IntegerField(choices=BEHAVIOR_CHOICES, default=1, verbose_name='行为类型') create_time = models.DateTimeField(auto_now_add=True, verbose_name='发生时间') class Meta: verbose_name = '用户行为' verbose_name_plural = verbose_name unique_together = ('user', 'item', 'behavior_type') def __str__(self): return f'{self.user.username}-{self.item.name}-{self.get_behavior_type_display()}'

这里有个容易踩的坑:如果不对行为表加唯一约束,同一个用户对同一件商品反复浏览会产生大量重复记录。算法在做相似度计算时,重复数据会无形中放大该商品的权重,导致推荐结果失真。所以unique_together这个约束一定要加上。如果还是担心重复浏览会冲掉之前的评分记录,可以在视图函数里用get_or_create逻辑做更新。

3. 基于物品的协同过滤算法实现

算法部分是整套源码里最有"含金量"的地方。我见过太多同学的代码里算法模块只有不到一百行,逻辑还写得糊里糊涂。这一章我按一个写法套路给你完整拆开,保证你看完就能在原项目的基础上改出属于自己的版本。

3.1 相似度计算方式的选择:余弦相似度怎么算

协同过滤里计算物品相似度,最常用的方式是余弦相似度。打个比方:有两个商品A和B,我们不去管它们的属性标签,只看"哪些用户对它们产生过行为"。如果对A有行为的用户集合和对B有行为的用户集合高度重合,那么A和B就是相似的。

余弦相似度的数学公式是:

similarity(A, B) = (A · B) / (||A|| × ||B||)

在推荐系统的实际代码里,我们通常会构建一个"用户-物品"评分矩阵。矩阵的行是用户,列是物品,值是该用户对物品的评分。如果用户没有对物品产生过行为,对应的位置就填0。有了这个矩阵,每个商品其实就对应一个列向量,两个商品之间的余弦相似度就是这两个列向量的余弦夹角。

你可能要问,为什么不是用欧几里得距离?余弦相似度更看重方向上的一致性,对评分数值的大小不敏感。比如用户A给商品X打了5分、给商品Y打了4分,用户B给X打了1分、给Y打了1分。欧氏距离会认为这两组数据离得远,但实际上X和Y仍然可能是相似的(用户对X和对Y的态度相对一致)。而在实际推荐场景里,我们恰恰更关心"用户对两个商品的态度是否一致",所以余弦相似度是更惯用的选择。

如果觉得向量太大不好计算,还有两个工程上的常用词可以写在论文里:一是"物品-物品共现矩阵",二是"归一化处理"。共现矩阵的意思是:两个物品同时出现在同一个用户的行为列表里,就计一次共现。归一化则是把原始的评分或行为次数压缩到0-1区间,防止热门商品过于强势地霸占推荐列表。

3.2 核心算法代码参考与解释

因为要从数据库里取数据、要做矩阵、要做计算,算法的纯Python实现即便在Demo规模下也要注意性能。不过在实际项目里,我通常用的是Pandas配合NumPy来写,这样代码量少,可读性和性能也都过得去。

下面这份代码基于"用户-物品评分矩阵"来生成物品相似度矩阵,并返回某用户最感兴趣的Top-N推荐结果。

import numpy as np import pandas as pd from django.core.cache import cache def build_user_item_matrix(): """ 从UserRating表读取行为数据,构造成用户-物品评分矩阵。 这里把购买行为记为5分,收藏记为3分,浏览记为1分,评分则直接用评分值。 """ from .models import UserRating records = UserRating.objects.exclude(item__isnull=True).values( 'user_id', 'item_id', 'rating', 'behavior_type' ) df = pd.DataFrame(list(records)) if df.empty: return pd.DataFrame() # 将行为类型转换为权重评分 behavior_weight = {1: 1, 2: 3, 3: 4, 4: 5, 5: 0} df['weight'] = df.apply( lambda row: row['rating'] if row['behavior_type'] == 5 and row['rating'] else behavior_weight.get(row['behavior_type'], 1), axis=1 ) # 同一个用户对同一款商品可能存在多条行为,取最大权重 df = df.groupby(['user_id', 'item_id'], as_index=False)['weight'].max() # 生成用户-物品透视矩阵,空值填0 matrix = df.pivot(index='user_id', columns='item_id', values='weight').fillna(0) return matrix def compute_item_similarity(matrix): """ 基于余弦相似度计算物品相似度矩阵。 matrix: 用户-物品矩阵,行是用户,列是商品 """ item_matrix = matrix.T # 转置后,行是商品,列是用户 norm = np.sqrt((item_matrix ** 2).sum(axis=1)).values.reshape(-1, 1) norm[norm == 0] = 1e-9 # 防止除零 item_norm = item_matrix / norm similarity = np.dot(item_norm, item_norm.T) item_ids = item_matrix.index.tolist() sim_df = pd.DataFrame(similarity, index=item_ids, columns=item_ids) return sim_df def recommend_for_user(user_id, top_n=10, max_sim_items=20): """ 为指定用户生成Top-N推荐。 策略:取该用户行为权重最高的若干商品,查找与这些商品最相似的物品, 排除用户已经产生过行为的物品,按相似度加权得分从高到低排序。 """ from .models import UserRating, Item matrix = build_user_item_matrix() if matrix.empty: # 如果行为数据全空,冷启动时按商品销量热度推荐 return Item.objects.order_by('-sales', '-created_at')[:top_n] sim_df = compute_item_similarity(matrix) # 获取该用户的行为数据 user_records = UserRating.objects.filter(user_id=user_id) user_interacted = set(user_records.values_list('item_id', flat=True)) if not user_interacted: return Item.objects.order_by('-sales', '-created_at')[:top_n] # 用户已交互物品的评分向量 user_vector = matrix.loc[user_id] if user_id in matrix.index else pd.Series(dtype=float) scores = {} for item_id in user_interacted: if item_id not in sim_df.columns: continue weight = user_vector.get(item_id, 1) if weight <= 0: weight = 1 similar_items = sim_df[item_id].sort_values(ascending=False).iloc[1:max_sim_items+1] for candidate_id, sim_score in similar_items.items(): if candidate_id in user_interacted: continue scores[candidate_id] = scores.get(candidate_id, 0) + sim_score * weight if not scores: return Item.objects.order_by('-sales', '-created_at')[:top_n] sorted_items = sorted(scores.items(), key=lambda x: x[1], reverse=True)[:top_n] item_ids = [item_id for item_id, _ in sorted_items] return Item.objects.filter(id__in=item_ids)

这份代码已经是"工程化地简化"过的版本了,但核心思路全都在。有一点需要注意:build_user_item_matrix每次推荐都要全量计算一遍矩阵和相似度,在Demo里问题不大,但如果数据量大了,页面响应速度会非常慢。所以我在实际带学生的项目里,都会把相似度矩阵的计算结果存到缓存里(代码里我引入了Django cache),设置一个过期时间,比如600秒。这样每个用户的推荐请求只会做一次矩阵乘法,而不是每来一个用户就重建一次矩阵。

提示:如果你在论文里想写"为什么选择余弦相似度而不是皮尔逊相关系数",最省力的说法是:余弦相似度计算简单、易于扩展,且对于稀疏评分矩阵的表达效果足够好;皮尔逊相关系数需要先对用户评分做均值中心化,在用户行为较少的情况下反而容易产生噪声。这个结论写在论文里是非常稳妥的。

3.3 冷启动问题的处理和答辩话术

几乎所有做推荐系统毕设的同学,都躲不开评审老师的一个问题:"系统刚上线,用户没有行为记录,你的推荐怎么做?"

这个问题在答辩提纲里基本属于必问题。如果你没有准备,现场很容易卡壳。实操上,我们的系统在Demo阶段就有三套兜底策略:

第一,热门推荐。新用户进入推荐页后,系统按商品销量和浏览热度排序,从销量最高的商品里挑出一批来展示。代码里我已经写了这个逻辑——Item.objects.order_by('-sales', '-created_at')。

第二,分类偏好推荐。注册时或首次使用系统时,让用户选择自己感兴趣的商品分类(服装、数码、图书等)。之后在没有行为数据的情况下,推荐系统可以直接按用户选择的分类推送商品。

第三,随机加热门混合。这个比较糙,但适合数据极少的初期阶段。系统随机取一些商品和热门商品混合展示,尽可能让用户产生点击行为——只要点了一下,后面的协同过滤就有数据可用了。

答辩时的标准话术可以这么说:"针对冷启动场景,本项目采用了基于商品热度的基础推荐策略来填充新用户的推荐页面,同时通过用户注册时选择的兴趣标签完成个性化初步匹配。随着用户行为数据的积累,推荐系统会平滑地从冷启动策略过渡到协同过滤算法。"这段话既解释了方案,又体现了设计思路的完整性。

4. Django项目环境搭建与关键模块配置

如果你想从零开始把这个项目复现出来,环境搭建和Django关键配置会决定你后面顺畅不顺畅。这里我按实际操作顺序来讲,不走弯路。

4.1 开发环境配置清单

一个标准的Django推荐系统Demo,依赖项通常如下:

依赖版本建议用途
Python3.8 - 3.11开发语言
Django3.2 - 4.2Web框架
MySQL / SQLiteMySQL 5.7+ / 内置数据存储,毕设我用SQLite居多
NumPy1.24+矩阵计算
Pandas2.0+数据处理
Pillow10.x图片上传处理

如果你是纯新手,强烈建议先装一个虚拟环境再动手:

python -m venv rec_env rec_env\Scripts\activate # Windows # source rec_env/bin/activate # macOS / Linux pip install django==4.2 numpy pandas pillow mysqlclient

不要图省事直接pip install全部依赖到全局环境。Python版本混乱、包冲突是毕业生电脑上的老戏码了,到时候报错排查半天,浪费时间还影响心态。

4.2 Django项目的目录规划与自定义配置

新建项目和应用的结构我建议这样:

recommend_system/ # 项目根目录 ├── manage.py ├── recommender/ # Django项目配置目录 │ ├── settings.py │ ├── urls.py │ └── ... ├── apps/ │ ├── users/ # 用户模块 │ ├── items/ # 商品模块 │ ├── orders/ # 订单模块 │ └── recommend/ # 推荐算法模块 ├── static/ # 静态资源 ├── media/ # 商品图片上传目录 ├── templates/ # 页面模板 └── scripts/ └── init_data.py # 初始化演示数据脚本

有几个配置项是务必检查的,否则后续一定会踩坑:

  • settings.py里的INSTALLED_APPS要注册所有的app;
  • TEMPLATES配置里的DIRS要指向项目根目录下的templates文件夹;
  • STATIC_URL和MEDIA_URL、MEDIA_ROOT要配好,否则商品图片传上去再访问,直接给你404;
  • 如果用了自定义用户模型,AUTH_USER_MODEL必须在你第一次迁移之前就配置好,否则Django会报AttributeError,而且几乎没法回退,只能重建数据库。
# settings.py 中一段关键配置 import os from pathlib import Path BASE_DIR = Path(__file__).resolve().parent.parent INSTALLED_APPS = [ 'django.contrib.admin', 'django.contrib.auth', 'django.contrib.contenttypes', 'django.contrib.sessions', 'django.contrib.messages', 'django.contrib.staticfiles', 'apps.users', 'apps.items', 'apps.orders', 'apps.recommend', ] MEDIA_URL = '/media/' MEDIA_ROOT = os.path.join(BASE_DIR, 'media') STATIC_URL = '/static/' STATICFILES_DIRS = [os.path.join(BASE_DIR, 'static')]

4.3 演示数据初始化脚本的设计

这个步骤很容易被忽略,但实际上是整个项目演示顺不顺畅的前提。很多同学拿到一套源码,结果数据库里空空如也,用户登录进去点了一圈发现推荐页什么都没有,也看不出任何推荐效果。比如你拿到的完整源码里,通常会自带一批演示数据——商品几十个、分类十几个、模拟用户若干,并且每个模拟用户都预置了几十条评分和购买记录。

手工往数据库里一条一条录入演示数据显然不现实。我当时在某个模拟项目X里用的是Django管理命令的方式,写一个自定义命令init_demo_data,批量造数据:

python manage.py init_demo_data

这个命令做的事情很简单:

  • 创建若干商品分类;
  • 每个分类下生成若干商品;
  • 创建测试用户账号(如demo1/demo2,密码统一设为123456);
  • 替这些用户随机生成一批浏览、收藏、购买行为,并保证每个用户至少对几个商品有共同交互。

测试账号和密码一定要写进你的实验说明文档里。答辩演示时,老师经常懒得注册新账号,你如果直接说"点这里用演示账号登录",体验会顺畅很多。

提醒:造演示数据时,行为数据不要太稀疏。我见过有同学导入了几百个商品,但每个用户只有一两条行为记录,结果推荐结果完全随机、毫无逻辑。稳妥的做法是每个用户至少产生20条行为数据,并且让不同用户之间的交互商品有30%左右的重合,这样算法算出来的相似度才有可见的规律。

5. 核心页面实现与推荐结果展示

项目的页面数量不用多,但关键页面的逻辑必须闭环。这一章我挑最要紧的页面逐个讲清楚,顺带指出哪些地方是可以拿来加分的。

5.1 首页与商品列表页:功能闭环的入口

首页在推荐系统里有两个使命。第一,它是一张"系统功能展示脸",分类导航、搜索框、商品展示位都要有;第二,它要能承接推荐算法的结果——通常是把"热门推荐"或者"猜你喜欢"放在首页最显眼的区域。

商品列表页则相对常规:左侧是分类筛选,中间是商品卡片网格,支持按价格、销量、上架时间排序。商品卡片上要有明确的"加入购物车"和"收藏"按钮,这两个按钮就是产生行为数据的入口。如果你的项目中这两个按钮没有接行为记录逻辑,那推荐系统就是空中楼阁。

商品详情页是最容易被忽略的推荐位。在详情页底部放一个"相关推荐"模块,调用recommend_for_user接口,根据当前商品找出相似的几款商品展示出来。这一处比首页推荐更能给评委留下深刻印象,因为它的推荐逻辑是肉眼可见的关联性。

5.2 推荐结果页:让算法效果"看得见"

推荐页是最有"演示效果"的一个页面。这里我强烈建议你不要只扔一个商品列表就完事。两个小设计能让效果提升一个档次:

第一,每一件推荐商品下方加一行小字,说明推荐理由。比如"因为您浏览过某品牌某型号,我们为您推荐这款"或"购买了该分类商品的用户也购买了以下商品"。这一行小字在答辩演示时简直可以直接拷打老师的心智——一眼就能看出系统不是随机推的。

第二,推荐结果区做一个"刷新推荐"按钮。点击后前端发起AJAX请求,后端重新调用算法,返回更新后的推荐列表。这个交互在演示中非常讨喜,因为它直观展示了算法是"活的",而不是写死的静态页面。

实现上,推荐页面与Django视图的配合大致如下:

from django.shortcuts import render from django.http import JsonResponse from .utils import recommend_for_user def recommend_page(request): user = request.user if not user.is_authenticated: # 未登录用户,走默认热门推荐 items = Item.objects.order_by('-sales')[:10] return render(request, 'recommend/guess_you_like.html', {'items': items}) items = recommend_for_user(user.id, top_n=10) return render(request, 'recommend/guess_you_like.html', {'items': items}) def recommend_refresh_api(request): if not request.user.is_authenticated: return JsonResponse({'code': -1, 'msg': '请先登录'}) items = recommend_for_user(request.user.id, top_n=10) data = [ { 'id': item.id, 'name': item.name, 'price': str(item.price), 'cover': item.cover_image.url if item.cover_image else '', 'reason': '根据您的浏览与购买记录推荐', } for item in items ] return JsonResponse({'code': 0, 'data': data})

5.3 管理后台的定制技巧

Django Admin自带的后台管理面已经够用,但直接交上去会显得项目没有"精心打磨"。花半天时间做三块定制,答辩时观感会上升一个台阶。

第一,列表页字段定制。用list_display控制后台列表展示哪些字段,用list_filter增加分类、状态等筛选器。别小看这些简单配置,它们能让管理后台看起来更像一个真实的后台系统。

from django.contrib import admin from .models import Item, Category, UserRating @admin.register(Item) class ItemAdmin(admin.ModelAdmin): list_display = ['id', 'name', 'category', 'price', 'stock', 'sales', 'created_at'] list_filter = ['category', 'created_at'] search_fields = ['name', 'description'] list_per_page = 20 @admin.register(UserRating) class UserRatingAdmin(admin.ModelAdmin): list_display = ['user', 'item', 'behavior_type', 'rating', 'create_time'] list_filter = ['behavior_type', 'create_time']

第二,增加一个"推荐数据概览"的统计面板,用Django Admin的admin.site.index_template或者自定义一个视图,展示用户总数、商品总数、行为记录总数、评分分布等。这部分工作量不大,但在论文里能截图放进去,作为"系统功能展示"中的高光一页。

第三,行为记录的可视化。如果你能做到这一步,项目已经超过80%的同题同学了。用pyecharts或者Chart.js在前端画一个简单的"用户行为频率柱状图",展示浏览/收藏/购买的行为分布。这个小模块不涉及任何复杂链路,但答辩时完全可以作为亮点讲出来。

6. 常见问题与调试技巧实录

这部分送给你的是我在无数次调试中攒下来的经验。每一条都是踩过坑之后才知道的,写在这帮你省时间。

6.1 推荐结果一片空白的排查思路

我在给某家公司内部演示推荐系统时,就遇到过推荐页输出空白的情况。大家千万别慌,按照从数据到算法的顺序排查就行:

第一步,检查UserRating表里有没有数据。如果数据为空,推荐函数必然走兜底逻辑,而如果你连兜底逻辑都没写,页面肯定空白。检查方式很简单,在Django shell里执行UserRating.objects.count()看看返回值。

第二步,检查用户是否已登录。recommend_for_user会针对当前用户查行为数据,如果用户未登录或者user_id不对,同样拿不到推荐。有的同学把request.user.is_authenticated的判断放在模板里而不是视图里,结果用户没登录就直接渲染了一个空列表。

第三步,检查算法返回的item_id列表,是不是包含已经被下架或删除的商品。Item.objects.filter(id__in=item_ids)如果过滤后是空QuerySet,页面自然什么都没有。做演示数据初始化时务必确保不再引用失效的item_id。

6.2 物品相似度矩阵太大导致响应缓慢

纯Python循环计算相似度矩阵在数据量小的时候无所谓,但当商品数量达到几百个时,三重循环的时间复杂度会让页面卡出天际。

解决办法我已经在前面提到了:把相似度矩阵的计算结果缓存到Django Cache里。另一种方案是把相似度矩阵落库到一张专门的ItemSimilarity表里,商品数据变化不频繁的情况下,每晚用定时任务重新计算一次即可。方案各有利弊,论文里通常不需要写到这个细节,但如果你写了推荐模块的"性能优化"小节,这会是很好的素材。

from django.core.cache import cache def get_similarity_matrix_with_cache(): key = 'item_similarity_matrix' sim_df = cache.get(key) if sim_df is None: matrix = build_user_item_matrix() sim_df = compute_item_similarity(matrix) cache.set(key, sim_df, timeout=600) return sim_df

6.3 数据库迁移时出现的混乱错

新手最常见的报错之一,是迁移时提示AttributeError: 'str' object has no attribute 'objects',或者"You need to specify 'default' value for field"。这两种报错的原因基本都是模型字段定义不严谨,或者是历史迁移文件脏了。

如果你用的是MySQL,还容易遇到中文不乱码或者时区不统一的问题。虽然DEMO阶段用SQLite可以规避大量麻烦,但如果你把项目最后部署到服务器给导师在线看效果,MySQL还是更稳妥。搭配Django的时区配置:

TIME_ZONE = 'Asia/Shanghai' USE_TZ = True

记住一个原则:模型字段的null=True和blank=True不要混用。对字符串字段,blank=True对应表单校验允许为空;对数值字段,null=True对应数据库允许NULL。写反了的人很容易在数据写入时报NOT NULL constraint failed。

6.4 商品图片无法访问的经典问题

这个问题的坑点其实不大,但几乎每个人都会遇到。如果你在商品页面能正常展示文字,但图片加载报404,请依次检查:

  • 图片是否真的上传到了MEDIA_ROOT指向的目录;
  • urls.py里有没有挂载media路由:
from django.conf import settings from django.conf.urls.static import static urlpatterns = [ # ... 其他路由 ] if settings.DEBUG: urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
  • models.py的ImageField里有没有设置upload_to,并且Pillow库是否已安装。

只要这三步查完,图片问题必解。

7. 关于毕设交付与答辩准备的补充建议

写到这里,我猜你可能已经对系统该怎么做了然于心。但毕设项目远不止"把系统跑起来"这么简单,文档、演示视频、答辩语术这几个环节同样要提前准备。

7.1 远程调试和讲解服务能帮你解决什么问题

我在对接某位同学的需求时,发现一个常见现象:代码在自己的电脑上跑得没问题,但一到老师指定的远程服务器上部署就各种报错。环境变量、Python版本、数据库连接方式、静态文件路径,任何一个环节都能卡住人。市面上标注"远程调试+讲解+定制"的服务,做的就是帮你消除环境差异带来的不确定性。如果条件允许,购买这类服务时建议至少包含以下交付内容:

  • 远程部署完成后,确保系统从登录到推荐全流程可跑通;
  • 核心算法部分(特别是相似度计算和推荐流程)能口头讲解清楚;
  • 演示数据初始化脚本和测试账号都已经就位。

用一句话总结就是:宁可多花时间在上线前的调试上,也不要把问题留给答辩现场。

7.2 论文里应该重点突出的三个创新点

写系统设计类论文时,很多同学容易陷入"流水账式"记录。我强烈建议你的设计章节围绕以下三个创新点展开:

第一,数据建模层面:通过统一的行为数据表完成"评分+行为+时效"三要素的建模。普通的商城系统只记录订单,而你的系统记录了用户从浏览到购买的完整链路,这正是推荐系统研究所需的"用户反馈偏好"。

第二,算法实现层面:实现了两种相似度计算方式并做了对比实验。如果论文里有【对比实验】的图表,说明你在设计阶段就想到了验证环节,这个细节很加分。

第三,工程落地层面的冷启动策略。很多同类课题都忽视冷启动问题,而你在设计文档里把这个讲透,可以体现你在系统设计阶段的整体思考。

7.3 最后的经验:演示几十遍也不嫌多

每次给导师演示前,我建议你先自己从头到尾走一遍全流程:注册新账号、浏览几个商品、下单购买、查看推荐页、刷新推荐、进入后台管理。特别是"注册新账号产生行为数据"这个链路,最考验系统鲁棒性,也最容易暴露隐藏bug。

我个人体会比较深的一点是:推荐系统这种课题,最怕的不是算法被问倒,而是演示时系统突然崩了。答辩老师最关心的是"你的系统能不能用",你的代码只要逻辑清晰、数据完整、演示顺畅,就已经赢了大半。至于算法精度是不是业界顶级——那从来都不是毕业设计评审的衡量标准。

最后再分享一个小技巧:把推荐结果页面也做成可导出的数据报告。哪怕只是简单的CSV,把推荐的商品ID、得分、推荐理由导出来,答辩时老师要看"数据长什么样",你直接拉出来一张表,这种细节往往比讲十页PPT都管用。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询