
简介这是一套面向高校计算机相关专业毕业设计的电影推荐系统完整源码采用PythonDjango作为后端框架、MySQL作为数据存储适合正在准备毕设或课程设计的学生参考与二次开发。资源包共约2000个文件以py源码、mo与po多语言文件、pyc编译文件为主另含html模板、js脚本、css样式、jpg图片及csv数据集等覆盖前后端与数据层压缩包约28.03MB。目前已有7816人学习下载热度较高。读者可从中获取推荐算法实现思路、Django项目分层结构、数据库表设计与前后端交互逻辑并借助多语言与静态资源文件理解完整工程组织方式便于快速搭建可运行环境、对照修改功能模块为论文撰写与答辩演示提供可落地的参考方案。1. 电影推荐系统毕设从一份 Django 源码里拆出可复现的推荐链路很多同学拿到「pythondjangomysql电影推荐系统源码毕业设计.zip」这类压缩包时第一反应是解压、装依赖、跑起来看首页能不能打开然后截图交差。但真正答辩时被问一句「你的推荐结果是怎么算出来的」往往就卡住了。这个标题背后其实是一条完整的工程链路Python 做数据处理与算法Django 做 Web 层与接口MySQL 存用户、电影、评分和推荐结果推荐算法负责把「用户可能喜欢的电影」算出来。它适合两类人一类是正在做计算机毕业设计、需要一套能跑通、能讲清、能改动的系统另一类是刚学完 Django 想找一个有真实业务逻辑的项目练手。这篇笔记不假设你手里那份源码长什么样而是按这类系统最常见的落地方式把环境、数据、算法、接口和踩坑点一层层拆开让你无论拿到哪份源码都能自己判断它值不值得改、该怎么改。2. 环境与数据底座Python、Django、MySQL 三件套怎么配才不返工2.1 版本选择与虚拟环境隔离这类毕设项目最常见的翻车点不是代码写错而是环境版本对不上。Django 2.x 和 4.x 在路由写法、url()与re_path()上有差异MySQL 5.7 和 8.0 在认证插件上也不一样。我一般会先确认源码里的requirements.txt或settings.py再决定装哪个版本。如果没有明确说明按下面这套组合走兼容性最好# 创建独立虚拟环境避免污染系统 Python python -m venv venv # Windows 激活 venv\Scripts\activate # macOS / Linux 激活 source venv/bin/activate # 安装核心依赖版本按源码实际需求调整 pip install django3.2.20 pip install mysqlclient2.2.0 pip install pandas1.5.3 pip install numpy1.24.3 pip install scikit-learn1.2.2这里mysqlclient是 Django 连接 MySQL 最常用的驱动比pymysql性能好但在 Windows 上需要本地有 MySQL 的开发库装不上时可以退回pymysql并在__init__.py里加pymysql.install_as_MySQLdb()。pandas和numpy用于读取电影评分数据集、构建用户-物品矩阵scikit-learn用于余弦相似度等计算。版本不要盲目追新Django 3.2 是长期支持版和多数毕设源码兼容。提示虚拟环境目录不要提交到 Git也不要在里面放数据集否则压缩包会变得很大。2.2 MySQL 建库与 Django 连接配置MySQL 这边Windows 10 上装 8.0 时记得选Use Legacy Authentication Method否则mysqlclient连接会报Authentication plugin caching_sha2_password cannot be loaded。装完后建库-- 创建数据库字符集用 utf8mb4 支持中文电影名 CREATE DATABASE movie_recommend DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci; -- 创建专用用户避免直接用 root CREATE USER movie_userlocalhost IDENTIFIED BY Movie2024; GRANT ALL PRIVILEGES ON movie_recommend.* TO movie_userlocalhost; FLUSH PRIVILEGES;然后在 Django 的settings.py里配置DATABASES { default: { ENGINE: django.db.backends.mysql, NAME: movie_recommend, USER: movie_user, PASSWORD: Movie2024, HOST: 127.0.0.1, PORT: 3306, OPTIONS: { charset: utf8mb4, }, } }HOST写127.0.0.1而不是localhost可以避免部分系统下走 socket 连接导致的权限问题。OPTIONS里的charset必须和建库时一致否则中文电影名会变成乱码。配置完成后执行python manage.py migrateDjango 自带的用户、会话等表会自动建好。2.3 电影与评分数据的表结构设计推荐系统的核心表通常三张电影表、用户表、评分表。用户表可以直接用 Django 的auth_user电影表和评分表需要自己建。下面是一个经过多个项目验证的模型设计# models.py from django.db import models from django.contrib.auth.models import User class Movie(models.Model): title models.CharField(max_length200, db_indexTrue) genres models.CharField(max_length200) # 类型如 Action|Comedy year models.IntegerField(nullTrue, blankTrue) avg_rating models.FloatField(default0.0) # 预计算的平均分 rating_count models.IntegerField(default0) # 评分人数 class Meta: db_table movie class Rating(models.Model): user models.ForeignKey(User, on_deletemodels.CASCADE) movie models.ForeignKey(Movie, on_deletemodels.CASCADE) score models.FloatField() # 1~5 分 timestamp models.DateTimeField(auto_now_addTrue) class Meta: db_table rating unique_together (user, movie) # 防止重复评分 indexes [ models.Index(fields[user, movie]), ]db_indexTrue加在title上是因为搜索功能会频繁按片名查询。unique_together保证一个用户对同一部电影只能有一条评分避免推荐矩阵出现重复行。avg_rating和rating_count是冗余字段用空间换时间列表页排序时不用每次JOIN再聚合。数据导入可以用pandas读 CSV 后批量bulk_create比逐条save()快一个数量级。3. 推荐算法落地从评分矩阵到可解释的推荐结果3.1 协同过滤的最小实现与矩阵构建电影推荐系统最常用的算法是协同过滤分用户基和物品基两种。毕设里用户基协同过滤更常见因为解释起来直观找到和你口味相似的人把他们喜欢但你没看过的电影推给你。核心是构建用户-电影评分矩阵然后算用户之间的余弦相似度。import numpy as np import pandas as pd from sklearn.metrics.pairwise import cosine_similarity def build_user_movie_matrix(ratings_qs): 把评分 QuerySet 转成用户-电影矩阵 df pd.DataFrame(list(ratings_qs.values(user_id, movie_id, score))) if df.empty: return pd.DataFrame() # pivot 后缺失值填 0表示未评分 matrix df.pivot_table( indexuser_id, columnsmovie_id, valuesscore, fill_value0 ) return matrix def recommend_for_user(matrix, user_id, top_n10): 基于用户相似度推荐 top_n 部电影 if user_id not in matrix.index: return [] # 计算目标用户与其他用户的余弦相似度 user_vector matrix.loc[[user_id]] sim_scores cosine_similarity(user_vector, matrix)[0] # 排除自己取相似度最高的 K 个用户 sim_series pd.Series(sim_scores, indexmatrix.index) sim_series sim_series.drop(user_id).sort_values(ascendingFalse) similar_users sim_series.head(20).index # 加权汇总相似用户的评分 weighted matrix.loc[similar_users].T.dot(sim_series[similar_users]) sim_sum sim_series[similar_users].sum() if sim_sum 0: return [] scores weighted / sim_sum # 过滤掉目标用户已经评过分的电影 rated matrix.loc[user_id] scores scores[rated 0] return scores.sort_values(ascendingFalse).head(top_n).index.tolist()fill_value0是最简单的处理方式缺点是会把「未评分」当成「评了 0 分」实际项目中可以用用户平均分或电影平均分填充。cosine_similarity返回的是矩阵取[0]拿到目标用户对所有用户的相似度数组。head(20)是取最近邻数量这个值太小推荐不准太大计算慢20 到 50 之间比较稳妥。最后用rated 0过滤已看过的电影这是推荐系统的基本礼仪否则用户会觉得系统在重复推老片。3.2 把推荐结果写回 MySQL 并做缓存每次请求都实时算一遍协同过滤用户一多就会卡。常见做法是离线算好推荐结果存到一张推荐表里接口直接查表。表结构可以这样设计class Recommendation(models.Model): user models.ForeignKey(User, on_deletemodels.CASCADE) movie models.ForeignKey(Movie, on_deletemodels.CASCADE) score models.FloatField() # 推荐得分 reason models.CharField(max_length200) # 推荐理由如 相似用户喜欢 created_at models.DateTimeField(auto_nowTrue) class Meta: db_table recommendation unique_together (user, movie) indexes [models.Index(fields[user, -score])]离线任务可以用 Django 的manage.py自定义命令来跑# management/commands/generate_recs.py from django.core.management.base import BaseCommand from django.contrib.auth.models import User from myapp.models import Rating, Recommendation, Movie from myapp.recommender import build_user_movie_matrix, recommend_for_user class Command(BaseCommand): help 离线生成所有用户的推荐结果 def handle(self, *args, **options): matrix build_user_movie_matrix(Rating.objects.all()) if matrix.empty: self.stdout.write(评分数据为空跳过) return Recommendation.objects.all().delete() # 全量重建简单可靠 batch [] for user_id in matrix.index: movie_ids recommend_for_user(matrix, user_id, top_n20) for mid in movie_ids: batch.append(Recommendation( user_iduser_id, movie_idmid, score0.0, reason相似用户喜欢 )) Recommendation.objects.bulk_create(batch, ignore_conflictsTrue) self.stdout.write(f生成 {len(batch)} 条推荐)bulk_create配合ignore_conflictsTrue可以避免唯一约束冲突导致整批失败。全量删除再重建适合数据量不大的毕设场景生产环境一般用增量更新。跑完这个命令后前端接口只需要Recommendation.objects.filter(userrequest.user).order_by(-score)[:10]响应时间从秒级降到毫秒级。3.3 冷启动与推荐理由的补全新用户没有评分记录协同过滤直接失效这就是冷启动。毕设里最实用的兜底策略是热门推荐按avg_rating和rating_count加权排序取前 N 部。加权公式可以用avg_rating * log(rating_count 1)避免只有一两个人打满分的电影霸榜。import math from django.db.models import F def popular_movies(top_n10): movies Movie.objects.filter(rating_count__gt0) scored [ (m, m.avg_rating * math.log(m.rating_count 1)) for m in movies ] scored.sort(keylambda x: x[1], reverseTrue) return [m for m, _ in scored[:top_n]]推荐理由也别偷懒只写「猜你喜欢」。可以从相似用户里找一部双方都评过高分的电影拼成「喜欢《XXX》的用户也喜欢这部」答辩时这一句话就能体现你对推荐可解释性的理解。冷启动用户先走热门榜等积累到 5 条以上评分再切到协同过滤这个阈值可以根据数据稀疏度调整。4. 避坑与排查这类毕设源码最容易翻车的 5 个地方4.1 现象migrate时报Unknown database或连接被拒原因通常是 MySQL 服务没启动、库名写错或者用户权限只给了localhost但 Django 用127.0.0.1连接。解决先mysql -u movie_user -p -h 127.0.0.1手动连一次确认能进再看settings.py里的NAME是否和CREATE DATABASE一致权限用GRANT ALL ON movie_recommend.* TO movie_user127.0.0.1再授一次。4.2 现象中文电影名在页面上显示成问号或乱码原因是数据库、表、连接三层字符集不统一。解决建库用utf8mb4DjangoOPTIONS里加charset已有表可以用ALTER TABLE movie CONVERT TO CHARACTER SET utf8mb4;转换。导入 CSV 时也要确认文件本身是 UTF-8 编码用 Excel 另存为 UTF-8 再导。4.3 现象推荐结果每次刷新都不一样或者推荐列表为空前者通常是用了随机采样且没有固定随机种子或者每次请求都重新训练。解决离线生成推荐结果写库接口只读库。后者常见于评分数据太少用户-电影矩阵几乎全零相似度算出来都是 0。解决先检查Rating表有没有数据数据量少于 100 条时协同过滤没有意义直接走热门推荐。4.4 现象pip install mysqlclient在 Windows 上编译失败原因是缺少 MySQL Connector/C 或 Visual C Build Tools。解决最省事的办法是改用pymysql在项目__init__.py里加两行import pymysql pymysql.install_as_MySQLdb()然后在requirements.txt里把mysqlclient换成pymysql。功能上对毕设完全够用性能差异在数据量小时感知不到。4.5 现象Django 删除对象后关联评分没删掉留下脏数据原因是外键on_delete用了DO_NOTHING或者没设置。解决把Rating和Recommendation的外键都设成on_deletemodels.CASCADE删用户或电影时自动清理关联记录。如果已经产生脏数据用一条 SQL 清理DELETE FROM rating WHERE movie_id NOT IN (SELECT id FROM movie);执行前先备份。5. 进阶技巧用 Django 管理命令做一键数据校验与推荐效果验证毕设答辩前导师很可能让你现场演示「推荐准不准」。与其口头解释不如写一个校验命令把关键指标打出来。下面这个命令会检查数据完整性、评分分布并用留一法粗略评估推荐命中率# management/commands/check_recs.py import random from django.core.management.base import BaseCommand from django.contrib.auth.models import User from myapp.models import Rating, Recommendation from myapp.recommender import build_user_movie_matrix, recommend_for_user class Command(BaseCommand): help 校验推荐数据并评估命中率 def handle(self, *args, **options): total_users User.objects.count() total_ratings Rating.objects.count() total_recs Recommendation.objects.count() self.stdout.write(f用户数: {total_users}) self.stdout.write(f评分数: {total_ratings}) self.stdout.write(f推荐数: {total_recs}) if total_ratings 100: self.stdout.write(评分数据不足建议先导入数据集) return # 留一法每个用户随机藏一条评分看推荐能否命中 matrix build_user_movie_matrix(Rating.objects.all()) hit, total 0, 0 for user_id in random.sample(list(matrix.index), min(50, len(matrix.index))): user_ratings matrix.loc[user_id] rated_movies user_ratings[user_ratings 0].index.tolist() if len(rated_movies) 5: continue holdout random.choice(rated_movies) # 临时把这条评分置零模拟未看过 matrix.loc[user_id, holdout] 0 recs recommend_for_user(matrix, user_id, top_n20) matrix.loc[user_id, holdout] user_ratings[holdout] # 还原 total 1 if holdout in recs: hit 1 if total 0: self.stdout.write(f留一法命中率: {hit}/{total} {hit/total:.2%}) else: self.stdout.write(有效用户不足无法评估)这个命令的价值在于它把「推荐系统有没有效果」从玄学变成了一个可以打印出来的数字。命中率能到 15% 以上在毕设场景就算能交代了低于 5% 说明数据太稀疏或者相似度计算有问题。我一般会在答辩前跑三遍取平均避免随机性导致数字忽高忽低。另外matrix.loc[user_id, holdout] 0这行是临时修改记得还原否则会影响后续用户的评估——这个坑我踩过第一次跑出来命中率虚高查了半天才发现是没还原矩阵。还有一个习惯每次改完推荐逻辑先跑check_recs看指标变化再决定要不要保留这次改动。不要凭感觉调参top_n、最近邻数量、填充策略这三个参数每变一个都记录一次命中率慢慢就能摸到这套数据下的合理区间。希望这些能帮到你少走点我当年走过的弯路。本文还有配套的精品资源点击获取