用Django+Vue从零搭建一个短视频推荐网站(附完整源码和部署教程)
短视频平台的爆发式增长让个性化推荐成为技术热点。许多开发者想尝试构建自己的推荐系统,却常被全栈开发的技术栈和算法集成门槛劝退。本文将手把手带你实现一个具备核心推荐功能的短视频网站,涵盖从数据库设计到算法调优的全流程。不同于简单Demo,我们会重点解决实际开发中遇到的跨域配置、性能优化和冷启动问题。
1. 项目架构设计与技术栈选型
现代Web开发早已进入前后端分离的时代。我们选择Django作为后端API服务框架,主要看中其开箱即用的Admin管理系统和稳健的ORM层;而Vue 3的组合式API写法能让前端逻辑更清晰。这种技术组合既保证了开发效率,又能满足毕业设计或创业项目的技术深度要求。
核心组件分工:
- 前端:Vue 3 + Pinia状态管理 + Vite构建工具
- 后端:Django 4.2 + Django REST Framework + SimpleJWT认证
- 数据库:PostgreSQL(支持JSON字段存储视频标签)
- 推荐算法:Python的Surprise库实现协同过滤
- 部署:Nginx反向代理 + Gunicorn应用服务器
提示:虽然MySQL更常见,但PostgreSQL对JSON数据的原生支持能简化标签系统的实现
2. 后端核心模块实现
2.1 数据库模型设计
短视频平台的核心数据关系体现在用户、视频和交互行为三个维度。我们在models.py中定义以下关键模型:
from django.db import models from django.contrib.auth.models import AbstractUser class User(AbstractUser): watch_history = models.JSONField(default=list) # 记录观看视频ID class Video(models.Model): title = models.CharField(max_length=200) uploader = models.ForeignKey(User, on_delete=models.CASCADE) tags = models.JSONField(default=list) # 视频标签如["舞蹈","搞笑"] view_count = models.IntegerField(default=0) class UserBehavior(models.Model): user = models.ForeignKey(User, on_delete=models.CASCADE) video = models.ForeignKey(Video, on_delete=models.CASCADE) behavior_type = models.CharField( # 点赞/收藏/分享 max_length=20, choices=BEHAVIOR_CHOICES) created_at = models.DateTimeField(auto_now_add=True)2.2 RESTful API开发
使用DRF构建API时,需要特别注意分页和过滤的配置,这对推荐系统的性能影响很大:
# settings.py REST_FRAMEWORK = { 'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.PageNumberPagination', 'PAGE_SIZE': 10, 'DEFAULT_FILTER_BACKENDS': [ 'django_filters.rest_framework.DjangoFilterBackend' ], 'DEFAULT_AUTHENTICATION_CLASSES': ( 'rest_framework_simplejwt.authentication.JWTAuthentication', ) }推荐接口的核心视图逻辑:
class RecommendView(APIView): permission_classes = [IsAuthenticated] def get(self, request): # 获取用户历史行为数据 user_actions = UserBehavior.objects.filter( user=request.user).values_list('video_id', flat=True) # 调用推荐算法(后续章节实现) video_ids = recommend_engine(user_actions) # 序列化返回结果 videos = Video.objects.filter(id__in=video_ids) serializer = VideoSerializer(videos, many=True) return Response(serializer.data)3. 推荐算法集成
3.1 协同过滤实现
在recommendations目录下创建算法模块:
# recommendations/algorithms.py from surprise import Dataset, KNNBasic from surprise.model_selection import train_test_split import pandas as pd def build_collaborative_filtering(): # 从数据库加载用户行为数据 behaviors = UserBehavior.objects.all().values() df = pd.DataFrame(list(behaviors)) # 转换数据格式 reader = Reader(rating_scale=(0, 1)) data = Dataset.load_from_df( df[['user_id', 'video_id', 'behavior_weight']], reader) # 训练模型 trainset, testset = train_test_split(data, test_size=0.25) algo = KNNBasic(k=40, sim_options={'user_based': True}) algo.fit(trainset) return algo3.2 冷启动解决方案
新用户或新视频的冷启动问题可以通过以下策略缓解:
热门推荐:当用户行为数据不足时返回近期热门视频
def get_trending_videos(): return Video.objects.order_by('-view_count')[:100]标签传播:基于视频标签的相似度推荐
from sklearn.metrics.pairwise import cosine_similarity def tag_based_recommendation(video_id): target = Video.objects.get(id=video_id) all_videos = Video.objects.exclude(id=video_id) # 计算标签相似度 similarities = [] for v in all_videos: sim = cosine_similarity([target.tags], [v.tags])[0][0] similarities.append((v.id, sim)) return sorted(similarities, key=lambda x: x[1], reverse=True)[:10]
4. 前端工程化实践
4.1 Vue组件设计
采用Composition API组织代码,保持逻辑清晰:
<script setup> import { ref, onMounted } from 'vue' import { useUserStore } from '@/stores/user' const userStore = useUserStore() const videos = ref([]) const fetchRecommendations = async () => { const res = await axios.get('/api/recommend/', { headers: { Authorization: `Bearer ${userStore.token}` } }) videos.value = res.data } onMounted(() => { fetchRecommendations() }) </script>4.2 性能优化技巧
视频懒加载:
<video v-for="video in videos" :key="video.id" :poster="video.cover_url" preload="none" @click="handleVideoClick" ></video>请求节流:
import { throttle } from 'lodash' window.addEventListener('scroll', throttle(() => { if (nearBottom()) loadMore() }, 500))
5. 生产环境部署
5.1 Nginx配置要点
处理视频流媒体需要特别注意缓冲区配置:
server { listen 80; server_name yourdomain.com; location /media/ { alias /path/to/media/; # 视频文件缓存设置 expires 30d; add_header Cache-Control "public"; } location / { proxy_pass http://127.0.0.1:8000; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; } }5.2 Gunicorn调优参数
根据服务器CPU核心数设置worker数量:
gunicorn core.wsgi:application \ --workers $(($(nproc) * 2 + 1)) \ --bind 0.0.0.0:8000 \ --timeout 120 \ --keep-alive 56. 常见问题解决方案
跨域问题:
在Django中安装django-cors-headers并配置:
# settings.py INSTALLED_APPS += ['corsheaders'] MIDDLEWARE.insert(2, 'corsheaders.middleware.CorsMiddleware') CORS_ALLOWED_ORIGINS = [ "http://localhost:8080", "https://yourdomain.com" ]视频上传限制:
调整Django的文件大小限制:
DATA_UPLOAD_MAX_MEMORY_SIZE = 1024 * 1024 * 100 # 100MB FILE_UPLOAD_MAX_MEMORY_SIZE = 1024 * 1024 * 100在项目开发过程中,最耗时的往往是前后端联调阶段。建议使用Postman先测试所有API接口,再进入前端开发。当推荐效果不理想时,可以尝试调整算法中的相似度计算方式,比如将余弦相似度改为皮尔逊相关系数。