ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

Python+Vue校园电影网站全栈开发:Django/Flask选型与m3u8播放

Python+Vue校园电影网站全栈开发:Django/Flask选型与m3u8播放 最近帮着朋友的学生梳理一个校园电影网站的完整开发流程从需求分析到前后端联调走了一遍发现这类项目的坑远没有表面看上去那么简单。标题挂的是“python基于vue的校园电影网站的设计与实现django flask pycharm”参数看着很多其实就是经典的 Python 后端 Vue 前端的全栈小项目技术栈属于课程设计、毕业设计、自学练手的高频组合后端在 Django 和 Flask 之间二选一前端用 Vue 做单页应用开发工具放到 PyCharm 里统一管理。这套组合能解决的问题很具体做一个能浏览电影、看详情、看播放、注册登录、发评论、收藏电影的校园门户网站。适合刚学完 Python 语法、想完整走一遍全栈流程的同学也适合时间紧张、需要快速出效果再应付答辩的在校生。这类项目真正的价值不在“能跑”而在于让你把 Python 后端、关系型数据库、前端框架、HTTP 接口这些东西串联起来明白一个网站从数据库字段设计到浏览器里响应点击事件中间到底经历了多少层。读完你会发现最难的不是写代码是做选择和排错。下面我把整个设计思路、后端实现、前端页面、m3u8 播放、联调部署这段路上的细节全部展开。1. 先做选择题Django 还是 Flask前端要不要 Vue1.1 后端框架怎么选校园电影网站为什么我站 Django标题里同时出现了 Django 和 Flask说明选题的人大概率在这两个框架之间犹豫过。我的建议非常直接选 Django除非你的项目需求简单到只有三个页面否则 Flask 的自由度在中小型业务系统里帮不了你太多反而会让你花大量时间在“拼装”上。先看电影网站的硬性需求用户注册登录、电影列表、分类筛选、关键词搜索、评论、收藏、后台管理。这些功能在 Django 里基本是“开箱即用”或者“配置一下就有”的程度。用户认证部分 Django 自带的auth应用直接提供 User 模型、会话管理、密码加密后台管理用自带的django.contrib.admin注册一下模型就能在/admin/路径下管理电影数据数据库操作有 ORM学习成本低到你只需要会写 Python 类不用手写 SQLAPI 层再叠加一个djangorestframeworkDRF序列化、分页、过滤、权限控制全部组件化开发速度非常可观。Flask 的定位是“微框架”核心只有路由和模板渲染注册登录要自己接 Flask-Login、数据库要自己装 Flask-SQLAlchemy、后台管理要么自己写要么接 Flask-Admin、API 要自己写序列化逻辑。你说这些有没有现成库有。但库和库之间版本兼容、初始化顺序、配置写法完全由你自己掌控一个初始化顺序出错就够新手排查半天。校园电影网站这种业务规模恰好处于“Flask 要拼装很久”的尴尬区间不如直接用 Django 全家桶少做选择题。表格对比一下对比项DjangoFlask用户认证内置开箱即用需要 Flask-Login 自行集成ORM自带的 ORM 非常成熟天然接 SQLAlchemy需自行配置后台管理Admin 站点注册即用Flask-Admin功能有限API 开发DRF 生态完善手动写序列化或接 Marshmallow项目结构固定项目/应用结构完全自由自由度也是负担学习曲线稍陡但资料极多上手快深入难资料杂这里不是贬低 FlaskFlask 在轻量 API、微服务、极简工具类站点上有自己的位置。但校园电影网站是一个完整的业务系统选 Django 省下的时间足够你把精力放到前端和播放功能上。1.2 前端为什么单独上一个 Vue 工程很多做课程设计的人会问Django 自带模板语法为什么还要单独弄一个 Vue 项目直接在 Django 的 HTML 模板里写 Vue 不就行了技术上确实可以Django 的render返回 HTML然后在模板里塞一个script srcvue.global.js写几个v-for也能把数据渲染出来。但这样的做法只适合做“局部交互页面”一旦你的项目需要按路由切换页面、维护登录状态、在多个页面间共享电影数据模板渲染那套逻辑就会变得极其痛苦。前后端分离的真正好处是互不干扰后端只返回 JSON前端负责所有页面跳转和数据渲染任何一方出问题都能独立排查。从答辩角度讲“前后端分离 RESTful API”这个架构表述本身就比“模板渲染 AJAX”显得有含金量面试官问起来也有得聊。具体版本选择上新的项目直接上 Vue 3 Vite不要再纠结 Vue 2。Vue 3 的组合式 API 让代码组织更清晰Vite 的启动速度碾压 WebpackElement Plus 组件库对 Vue 3 支持也成熟。如果你的毕业设计模板是 Vue 2 那一套老代码建议迁移到 Vue 3成本比你想象的低。1.3 PyCharm 环境准备解释器、虚拟环境、前端终端PyCharm 是这一整套流程的根据地。先说 Python 环境我建议直接在 PyCharm 里创建一个虚拟环境不要用全局 Python 裸装依赖。打开 PyCharm新建项目在 New Environment 里选择 VirtualenvPython 解释器选到本机的 Python 3.10 或 3.11。虚拟环境会把 Django、DRF 全部装进项目目录下的venv文件夹里避免你以后做其他项目时版本冲突。PyCharm 版本这块提一嘴社区版免费对 Python 开发完全够用Vue 的前端代码在社区版里也能正常高亮和编辑。专业版对前端框架支持更好学生身份可以正常申请免费授权不要去碰什么破解激活工具正规渠道足够满足需求。如果你用的是社区版前端终端直接在 PyCharm 底部 Terminal 里跑Vite 的开发服务器照样能启动不受影响。依赖安装顺序建议先装后端再跑前端。后端依赖主要是 Django、djangorestframework、django-cors-headers、django-filter、djangorestframework-simplejwt。写进requirements.txt后用 pip 一并安装比逐个pip install省事得多。前端依赖在项目初始化后用 npm 安装。2. 后端搭建模型、接口、登录权限一锅端2.1 数据模型怎么设计上来不写模型直接写界面的人十有八九最后要返工。数据库模型是整个网站的地基先定模型再写序列化器和接口最后才是前端页面。校园电影网站的核心实体有五个用户、电影、分类、评论、收藏。用户直接继承 Django 自带的AbstractUser不要自己从零写一个 User 表自己写的用户模型基本不会比 Django 内置的安全机制更完善。电影模型是核心字段设计要考虑清楚标题、封面图、视频地址、简介、分类、上映年份、评分、创建时间。视频地址这里需要解释一下现在很多校园项目直接存一个 mp4 文件地址但扩展性更好的做法是存 m3u8 播放地址因为流媒体是当前在线视频的主流方案后面播放器部分我会细讲。from django.db import models from django.contrib.auth.models import AbstractUser class User(AbstractUser): avatar models.URLField(头像, blankTrue, default) created_at models.DateTimeField(auto_now_addTrue) class Category(models.Model): name models.CharField(分类名称, max_length50) created_at models.DateTimeField(auto_now_addTrue) def __str__(self): return self.name class Movie(models.Model): title models.CharField(电影名称, max_length200) cover models.URLField(封面图, blankTrue, default) video_url models.URLField(视频地址, blankTrue, default) category models.ForeignKey(Category, related_namemovies, on_deletemodels.SET_NULL, nullTrue) description models.TextField(简介, blankTrue) rating models.DecimalField(评分, max_digits3, decimal_places1, default0.0) release_year models.IntegerField(上映年份, blankTrue, nullTrue) created_at models.DateTimeField(auto_now_addTrue) updated_at models.DateTimeField(auto_nowTrue) def __str__(self): return self.title class Comment(models.Model): user models.ForeignKey(User, related_namecomments, on_deletemodels.CASCADE) movie models.ForeignKey(Movie, related_namecomments, on_deletemodels.CASCADE) content models.TextField(评论内容) created_at models.DateTimeField(auto_now_addTrue) def __str__(self): return f{self.user.username}: {self.content[:20]} class Favorite(models.Model): user models.ForeignKey(User, related_namefavorites, on_deletemodels.CASCADE) movie models.ForeignKey(Movie, related_namefavorited_users, on_deletemodels.CASCADE) created_at models.DateTimeField(auto_now_addTrue) class Meta: unique_together (user, movie)几个字段设计上的关键点说一下。ForeignKey里面的on_deletemodels.SET_NULL是为了防止分类被删除后电影一起消失对内容型网站更友好评论和收藏的on_deletemodels.CASCADE则是合理的用户删除账号后他的评论和收藏理应清空。Category居然是在Movie里定义的这里我刻意使用了“分类外键”放在电影中实现一个电影属于一个分类一个分类包含多部电影的一对多关系。如果你需要一部电影有多个分类就要改成多对多字段但校园电影网站的复杂度一般用不到。模型定义好以后执行两条命令生成数据库表python manage.py makemigrations python manage.py migrate然后在 PyCharm 的 Terminal 里打开 Django 的 shell 命令行模式可以手动插入几条测试数据验证模型是否正常。这个 shell 就是你以后测试 ORM 查询的地方特别是“执行查询-删除对象”这个高频需求在 Django shell 里验证清楚了再写进视图代码效率会高很多。2.2 用 DRF 把接口快速暴露出来模型建好之后接下来要做的是把数据库数据变成 JSON 接口。没有 DRF 的时候你需要自己在 Django 视图里用JsonResponse手动把 QuerySet 转成列表分页和过滤还要自己写工作量翻倍。DRF 直接把这个过程模板化。安装依赖之后在settings.py的INSTALLED_APPS里注册INSTALLED_APPS [ ... rest_framework, django_filters, corsheaders, ]中间件里加corsheaders.middleware.CorsMiddleware并设置CORS_ALLOW_ALL_ORIGINS True开发阶段的跨域问题就靠它先顶着后面部署要收紧配置。序列化器是 DRF 的核心它决定了 API 返回什么字段。电影列表页不需要返回大段简介但详情页需要所以序列化器字段要根据接口场景来设计。比较稳妥的做法是写一个基础序列化器再写一个详情序列化器from rest_framework import serializers from .models import Movie, Comment, Category class CategorySerializer(serializers.ModelSerializer): class Meta: model Category fields [id, name] class CommentSerializer(serializers.ModelSerializer): username serializers.CharField(sourceuser.username, read_onlyTrue) class Meta: model Comment fields [id, username, content, created_at] class MovieListSerializer(serializers.ModelSerializer): category_name serializers.CharField(sourcecategory.name, read_onlyTrue) class Meta: model Movie fields [id, title, cover, rating, release_year, category_name] class MovieDetailSerializer(serializers.ModelSerializer): category_name serializers.CharField(sourcecategory.name, read_onlyTrue) comments CommentSerializer(manyTrue, read_onlyTrue) class Meta: model Movie fields [id, title, cover, video_url, description, rating, release_year, category_name, comments]视图部分我推荐用ModelViewSet而不是APIView理由很简单ModelViewSet一个类就把列表、详情、创建、更新、删除五个接口全部覆盖然后再配合 DRF 的 DefaultRouter 自动生成 URL 路由。一是代码量少二是接口风格统一。from rest_framework import viewsets from rest_framework.pagination import PageNumberPagination from django_filters.rest_framework import DjangoFilterBackend from rest_framework.filters import SearchFilter, OrderingFilter from .models import Movie, Comment, Category from .serializers import (MovieListSerializer, MovieDetailSerializer, CategorySerializer, CommentSerializer) class MoviePagination(PageNumberPagination): page_size 12 page_size_query_param page_size class MovieViewSet(viewsets.ModelViewSet): queryset Movie.objects.all().order_by(-created_at) pagination_class MoviePagination filter_backends [DjangoFilterBackend, SearchFilter, OrderingFilter] filterset_fields [category] search_fields [title, description] ordering_fields [rating, release_year, created_at] def get_serializer_class(self): if self.action retrieve: return MovieDetailSerializer return MovieListSerializerfilterset_fields允许前端通过/api/movies/?category1做分类筛选search_fields允许/api/movies/?search寻梦做关键词搜索ordering_fields允许/api/movies/?ordering-rating按评分倒序排列。这三个功能覆盖了电影网站核心的浏览方式。路由配置也很简单from rest_framework.routers import DefaultRouter from django.urls import path, include from .views import MovieViewSet, CategoryViewSet, CommentViewSet router DefaultRouter() router.register(rmovies, MovieViewSet, basenamemovie) router.register(rcategories, CategoryViewSet, basenamecategory) router.register(rcomments, CommentViewSet, basenamecomment) urlpatterns [ path(api/, include(router.urls)), ]启动 Django 开发服务器后浏览器访问/api/movies/就能看到 JSON 数据前端那边的数据来源就绪了。2.3 JWT 登录与评论权限评论和收藏必须登录后才能操作这是校园论坛类产品的基本要求。前后端分离项目里推荐用 Token 认证而不是传统的 Session Cookie。理由很简单前端是独立部署的 Vue 应用跨域名请求时 Cookie 的处理比较麻烦Token 通过请求头传递则完全没有这种限制。热搜词里“django cookie 设置 token”指的基本就是这个需求。JWT 实现直接用djangorestframework-simplejwt配置复杂度非常低。在settings.py里加入REST_FRAMEWORK { DEFAULT_AUTHENTICATION_CLASSES: [ rest_framework_simplejwt.authentication.JWTAuthentication, ], DEFAULT_PERMISSION_CLASSES: [ rest_framework.permissions.IsAuthenticatedOrReadOnly, ], }IsAuthenticatedOrReadOnly表示未登录用户只能看登录用户可以写。GET 请求任意放行POST、PUT、DELETE 需要带 Token。视图里的权限控制可以直接用permissions.IsAuthenticated也可以重写perform_create方法来自动注入当前用户。评论接口的创建操作比较特殊因为评论必须关联到当前登录用户不能让用户在请求体里随便传一个 user_id否则会出越权问题from rest_framework import permissions, viewsets from rest_framework.response import Response from .models import Comment from .serializers import CommentSerializer class CommentViewSet(viewsets.ModelViewSet): queryset Comment.objects.all() serializer_class CommentSerializer permission_classes [permissions.IsAuthenticatedOrReadOnly] def perform_create(self, serializer): serializer.save(userself.request.user) def get_queryset(self): movie_id self.request.query_params.get(movie) if movie_id: return self.queryset.filter(movie_idmovie_id) return self.querysetperform_create里用serializer.save(userself.request.user)把当前登录用户注入到评论数据里前端只需要传电影 ID 和评论内容这是一个非常关键的安全细节。get_queryset里加movie参数过滤前端在电影详情页只需要请求/api/comments/?movie1就能拿到当前电影的评论列表。simplejwt 的登录接口路径默认是/api/token/和/api/token/refresh/集成到项目 urls.py 里from rest_framework_simplejwt.views import TokenObtainPairView, TokenRefreshView urlpatterns [ path(api/token/, TokenObtainPairView.as_view(), nametoken_obtain_pair), path(api/token/refresh/, TokenRefreshView.as_view(), nametoken_refresh), ]前端拿到 access token 后放入后续请求的Authorization: Bearer token请求头即可。2.4 Django Admin 和 ORM 操作技巧Django Admin 是这类项目里最容易被低估的功能。数据模型注册进 Admin 之后你不需要写任何管理页面代码就可以通过后台直接增删改查电影、分类和用户。这对数据初始化和维护来说极其方便演示的时候也很有说服力。在admin.py里面注册from django.contrib import admin from .models import Movie, Category, Comment, Favorite admin.register(Movie) class MovieAdmin(admin.ModelAdmin): list_display [id, title, category, rating, release_year, created_at] list_filter [category, release_year] search_fields [title, description] ordering [-created_at]list_display决定后台列表展示哪些字段list_filter生成右侧筛选栏search_fields在页面顶部生成搜索框。这样后台就已经变成了一个完整的内容管理系统视频地址和封面图可以直接在后台填入。日常 ORM 操作有几点值得提醒。查询用filter、exclude、get删除对象可以用obj.delete()或queryset.delete()批量删除推荐后者更新字段可以用queryset.update(...)避免逐条修改。这些操作在 Django shell 里敲一遍比看十遍文档更管用。尤其当你需要删掉测试数据里的某个分类时记得先看这个分类下有没有电影因为外键的on_delete行为决定了子表数据会不会跟着被删。3. 前端实现从空页面到能正常看电影3.1 Vue3 项目初始化和依赖后端接口就绪后开始搭前端。前端工程创建用 Vite 官方脚手架在 PyCharm 的 Terminal 里执行npm create vitelatest film-frontend -- --template vue cd film-frontend npm install基础模板装好后按需追加依赖npm install vue-router4 pinia axios element-plus hls.jsvue-router管页面路由pinia管登录状态axios管 HTTP 请求element-plus提供 UI 组件hls.js解决 m3u8 视频播放。这些依赖足够支撑整个网站了React 系的东西一概不用避免给自己增加多余的知识负担。main.js里一次性挂载所有核心插件import { createApp } from vue import { createPinia } from pinia import ElementPlus from element-plus import element-plus/dist/index.css import App from ./App.vue import router from ./router const app createApp(App) app.use(createPinia()) app.use(router) app.use(ElementPlus) app.mount(#app)Element Plus 全量引入在开发阶段完全没问题等以后要性能优化了再考虑按需导入。这种小体量项目全量引用的首屏体积是不好看的但开发体验极佳这个取舍要拎得清。3.2 路由、状态和请求封装前端路由表要覆盖首页、电影列表、电影详情、播放页面、登录页、注册页、个人中心。Vue Router 4 的写法比较固定import { createRouter, createWebHistory } from vue-router const routes [ { path: /, name: home, component: () import(../views/HomeView.vue) }, { path: /movies, name: movies, component: () import(../views/MovieListView.vue) }, { path: /movies/:id, name: movie-detail, component: () import(../views/MovieDetailView.vue) }, { path: /login, name: login, component: () import(../views/LoginView.vue) }, { path: /register, name: register, component: () import(../views/RegisterView.vue) }, { path: /favorites, name: favorites, component: () import(../views/FavoritesView.vue) }, ] const router createRouter({ history: createWebHistory(), routes, }) export default router懒加载的() import()写法能让每个页面独立打包首屏只加载需要的组件。动态路由属于进阶玩法如果需求是分类从数据库动态生成可以改成router.addRoute但对校园电影网站来说静态路由表加路由参数:id已经完全够用。请求封装是让前端代码不散架的关键。在src/utils/request.js里创建一个 axios 实例统一配置 baseURL 和请求拦截器import axios from axios const api axios.create({ baseURL: /api, timeout: 10000, }) api.interceptors.request.use((config) { const token localStorage.getItem(token) if (token) { config.headers.Authorization Bearer ${token} } return config }) export default apilocalStorage里只存 token 和用户基本信息不要存敏感数据。这样所有请求自动带上登录凭证评论、收藏等接口不用每次都手动传 Token。后面如果做刷新 Token 的逻辑可以在响应拦截器里捕获 401 状态并自动调用刷新接口。登录状态用 Pinia 管理新建src/stores/user.jsimport { defineStore } from pinia export const useUserStore defineStore(user, { state: () ({ token: localStorage.getItem(token) || , username: localStorage.getItem(username) || , }), actions: { setUser(token, username) { this.token token this.username username localStorage.setItem(token, token) localStorage.setItem(username, username) }, logout() { this.token this.username localStorage.removeItem(token) localStorage.removeItem(username) }, }, })3.3 列表页和详情页的页面逻辑电影列表页是整个网站流量最大的页面数据加载逻辑要清晰。用onMounted里调用 API然后把结果交给响应式变量渲染script setup import { ref, onMounted } from vue import api from ../utils/request const movies ref([]) const loading ref(false) const currentPage ref(1) const total ref(0) const keyword ref() const categoryId ref() const fetchMovies async () { loading.value true try { const res await api.get(/movies/, { params: { page: currentPage.value, search: keyword.value || undefined, category: categoryId.value || undefined, }, }) movies.value res.data.results total.value res.data.count } finally { loading.value false } } onMounted(fetchMovies) /script这个列表页注意两点第一res.data的结构是 DRF 分页响应标准格式results是当前页数据count是总数第二搜索和分类筛选用同一个fetchMovies函数参数变了直接重新请求不用额外写逻辑。详情页的数据加载需要注意评论列表的展示时机。用详情接口一次返回电影信息和评论列表的方法最简单但评论区有新增评论要求新增完成后要么重新请求详情接口要么手动调用评论接口再 push 到数组里。我建议拆成两个请求详情调/movies/:id/评论调/comments/?movie:id这样新增评论后的刷新逻辑只影响评论区域体验更好。页面样式方面如果没有专业设计师同事直接用 Element Plus 的el-card、el-row、el-col组件拼布局比手写纯 CSS 快得多。封面图加载失败的情况要处理用onerror事件切换成默认占位图否则列表中某个图片挂了会导致整个卡片区域很难看。3.4 m3u8 视频播放的完整方案m3u8 是本次项目里最值得单独拿出来讲的部分。很多人在这个点上卡了很久因为浏览器原生video标签并不能直接播放.m3u8文件只有 Safari 浏览器因为内置了 HLS 支持才可以用。Chrome、Edge、Firefox 这些主流浏览器都需要借助 JavaScript 库把 HLS 转换为浏览器能识别的视频流。hls.js 就是干这个事的它是目前最成熟的纯前端 HLS 播放解决方案。播放页的封装代码script setup import { ref, onMounted, onBeforeUnmount } from vue import Hls from hls.js const props defineProps({ src: { type: String, required: true }, }) const videoRef ref(null) let hls null onMounted(() { const video videoRef.value if (!video) return if (Hls.isSupported()) { hls new Hls() hls.loadSource(props.src) hls.attachMedia(video) hls.on(Hls.Events.MANIFEST_PARSED, () { video.play() }) } else if (video.canPlayType(application/vnd.apple.mpegurl)) { video.src props.src } }) onBeforeUnmount(() { if (hls) { hls.destroy() } }) /script template video refvideoRef controls classvideo-player/video /template这里有个非常容易忽略的坑组件卸载时一定要调用hls.destroy()。Vue 路由切换后组件实例会被销毁如果不手动销毁 hls 实例视频流还会继续占用网络资源甚至因为事件监听器没有释放导致内存泄漏。我见过不少人的播放页面切出去再切回来视频就卡死根本原因就是 hls 实例没有正确销毁。另一个常见的坑是 m3u8 服务端的跨域限制。如果视频地址和前端不在同一个域名下服务端必须允许跨域请求否则 hls.js 拉不到视频流。开发阶段可以用 Vite 代理解决也可以在后端设置跨域头这个我放在联调部分具体展开。4. 联调部署与踩坑经验4.1 前后端联调和 CORS前端开发服务器默认跑在http://localhost:5173后端 Django 跑在http://localhost:8000两个端口不同意味着跨域。这个问题的处理方式有两条路开发阶段推荐用 Vite 的代理配置部署阶段再让后端统一输出静态文件。Vite 代理配置在vite.config.js里import { defineConfig } from vite import vue from vitejs/plugin-vue export default defineConfig({ plugins: [vue()], server: { proxy: { /api: { target: http://localhost:8000, changeOrigin: true, }, }, }, })这样前端代码里请求的/api/movies/会由 Vite 开发服务器转发到http://localhost:8000/api/movies/浏览器视角内不存在跨域后端的django-cors-headers也就成为冗余配置。但如果你前端部署时直接走 nginx后端也独立部署那么后端的跨域配置就要认真设置了。开发阶段可以CORS_ALLOW_ALL_ORIGINS True上线前改成白名单模式CORS_ALLOWED_ORIGINS [ http://your-frontend-domain.com, ]还有一个细节是 Django 里DEBUG True时允许所有 host一旦部署到线上需要改成ALLOWED_HOSTS列表否则会收到 DisallowedHost 异常。4.2 常见错误排查速查表写代码的过程就是排错的过程把我在实操中经常遇到的问题整理成一张表按出现频率排序错误现象可能原因解决方案ModuleNotFoundError: No module named djangoPyCharm 选了全局解释器而不是虚拟环境在 PyCharm 设置里把 Python Interpreter 切换到项目 venv前端请求/api/movies/网络 500Django 异常去后端终端看 Traceback通常序列化器字段写错或视图函数有 Bug前端请求 403 ForbiddenCSRF 验证失败DRF 项目中确认使用了JWTAuthentication并放行/api/token/m3u8 播放一直转圈跨域被拦截 或 视频地址失效 或 防盗链用浏览器开发者工具 Network 面板查看请求状态码中文写入数据库后乱码数据库字符集不是 utf8创建 MySQL 数据库时指定 utf8mb4SQLite 一般没这个问题端口被占用上一个开发服务器没关PyCharm 里关掉旧进程或使用lsof -i:8000查询占用进程ImproperlyConfigured: Empty STATICFILES_DIRSsettings 配置问题确保STATICFILES_DIRS指向存在的目录或者删除该项登录接口返回 404路由没注册检查urlpatterns是否包含api/token/路径这里必须强调一点遇到 500 错误时第一时间去后端跑开发服务器的终端窗口看完整的 Python Traceback。前端浏览器的 Network 面板只能告诉你响应码真正的错误信息在 Python 的后端终端输出里别浪费时间在前端瞎猜。4.3 如果坚持用 Flask最少跑通要多长代码虽然推荐 Django但标题既然把 Flask 也放在里面我就给一个最简可运行的 Flask 版本参考。如果你因为某些原因比如课程要求必须用 Flask下面的代码是底线from flask import Flask, jsonify, request from flask_sqlalchemy import SQLAlchemy from flask_cors import CORS app Flask(__name__) app.config[SQLALCHEMY_DATABASE_URI] sqlite:///movies.db db SQLAlchemy(app) CORS(app) class Movie(db.Model): id db.Column(db.Integer, primary_keyTrue) title db.Column(db.String(200), nullableFalse) cover db.Column(db.String(500), default) video_url db.Column(db.String(500), default) rating db.Column(db.Float, default0.0) app.route(/api/movies, methods[GET]) def movie_list(): movies Movie.query.all() return jsonify(movies[{ id: m.id, title: m.title, cover: m.cover, rating: m.rating, } for m in movies]) if __name__ __main__: with app.app_context(): db.create_all() app.run(debugTrue)这段代码能跑但你也看到了用户注册登录没有、评论没有、后台管理没有、搜索筛选没有。你可以后续把 Flask-SQLAlchemy 的模型扩展、Flask-JWT-Extended 的认证、Flask-Admin 的后台都加上但每加一个模块都要重新验证一遍兼容性开发周期比 Django 长不少。遇到项目开始时“两个框架都行”的纠结状态Flask 的好处是“上手快”坏处是“做完整系统什么都得自己搭”。如果是为了学习框架本身两个都该玩为了快速出成果交作业选 Django 不折腾。最后分享两个实操心得第一个心得是数据填充和测试要趁早。模型和 Admin 配好后尽早往后台里录 20 部电影的完整数据包括封面图、简介、视频地址把接口都测通了再开始写前端页面。很多人喜欢把前后端同时写结果前端一调接口发现字段名对不上反复返工。先把后端接口用浏览器或 Postman 测好字段名、分页结构、状态码全部确认前端开发会顺很多。第二个心得是播放地址别在开发阶段就依赖线上视频。本地测试时可以找一个公开的 m3u8 测试地址也可以自己用 ffmpeg 把 mp4 转成 m3u8 格式放到本地。这样可以排除视频源本身的问题确保前端播放器代码没问题后再去对接真正的视频地址。如果视频源经常抽风你会分不清是播放器代码的 Bug 还是视频服务的问题调试效率大打折扣。整个项目做到能跑、能播、能评论收藏基本就是这个标题下的完整交付物了。后续想扩展的话电影推荐算法、用户观影历史、管理员上传视频、评论点赞都是这个架构上能继续长出来的功能。先把地基打稳后面加什么都是水到渠成的事。
返回列表