资讯动态

Flask + Vue 前后端分离课程网站实战:从数据库设计到部署

发布时间:2026/9/15 22:01:56 来源:尧图企业网站定制
做了个小项目Flask Vue 的精品课程网站开发环境是 Pycharm中途顺带对比了 Django 的写法。这个项目前前后后花了我大概两周的业余时间踩了不少坑也沉淀了一些能直接用的经验。这篇文章不是那种官方的入门教程而是把我从零搭建、数据库设计、视频处理、前端联调、再到服务器部署的完整过程捋一遍哪些方案省事、哪些方案是坑、为什么这么选都写清楚。不管你是要用 Flask 还是 Django刚接触前后端分离还是已经在写小项目但想系统性梳理一遍这篇都适合你。注意文中涉及的操作我都基于 Flask 2.x Vue 3 为主Django 作为另一套同功能方案做了对比讲解代码片段都经过简化但核心逻辑是可以在自己机器上直接跑通的。1. 技术选型与项目整体设计思路很多人在选型的时候容易纠结其实核心就看你的项目规模和团队习惯。我这个课程网站的实际需求是用户注册登录、课程分类浏览、课程详情、视频播放、评论互动、后台管理课程数据量属于中小型没有高并发压力所以选型时重点考虑的是开发效率和维护成本。1.1 为什么选 Flask Vue而不是只用一个框架当时我对比了三套方案纯 Flask Jinja2 模板、Flask Vue 前后端分离、Django Vue 前后端分离。纯模板方案开发最快但课程网站的交互比较重——视频进度记录、评论实时展示、后台数据可视化这些用模板渲染会非常别扭。Jinja2 适合做服务端渲染的内容站但一旦前端逻辑复杂模板里塞 JavaScript 会变得难以维护。所以我选了前后端分离后端只提供 API前端用 Vue 负责所有交互。这样做的直接好处是课程列表、视频播放器、后台管理这三个模块可以独立开发互不干扰出 bug 时排查边界很清晰。Flask 作为后端比 Django 轻量一个课程网站所需的 API 数量并不多用 Flask 的蓝图模块化组织起来非常舒服开发调试周期比 Django 短。Django 我也在同一阶段尝试了同款功能它的优势在于自带 Admin 后台和 ORM 更强大写课程管理界面几乎不用自己动手。但如果你需要高度定制前端交互Django 内置模板和 Admin 反而会形成限制需要额外配置才能完全前后端分离。1.2 开发环境准备和合理配置先交代我的本地环境Windows 11Python 3.10Pycharm 2023.2 ProfessionalNode.js 16.20npm 8.19MySQL 8.0本地开发用 SQLite 过渡生产切 MySQLRedis 可选生产环境用在缓存Pycharm 用社区版也能做这个项目但专业版对 Vue 的支持好很多自带 Vue 插件、模板语法高亮、调试器直接识别前端代码。我开头用社区版做 Flask 部分没任何问题后来做 Vue 前端时发现缺少 JavaScript 调试支持补了专业版。提示Pycharm 打开前后端分离项目时建议把前端和后端作为两个独立的项目窗口打开或者在同一个窗口配置两个 Run/Debug 配置。不要用 Pycharm 直接打开整个项目根目录然后硬编前端目录否则模块解析经常会出问题。虚拟环境我用的是 Pycharm 自带的 virtualenvPython 解释器指向 3.10。Flask 装的是 2.3.xDjango 装的是 4.2.x。为什么不用最新版新版本往往刚发布时第三方库兼容性还没跟上比如 Django 5.x 对 mysqlclient 的支持在写这篇文章时还没有完全稳定。做项目求稳选经过社区验证的版本是更明智的。2. 数据库设计和后端 API 实现一个课程网站核心的数据模型按我的划分是五大块用户、课程分类、课程、章节视频、评论。这五块关系清晰适合示范如何设计 RESTful API。2.1 用户模块和 Token 认证用户表我用的是 Flask 的扩展 Flask-SQLAlchemy 定义相比 Django 的模型代码上两者其实长得非常像。唯一区别是 Flask 需要自己安装扩展而 Django 的django.contrib.auth内置了用户认证体系。在 Flask 中的用户模型from flask_sqlalchemy import SQLAlchemy from werkzeug.security import generate_password_hash, check_password_hash from datetime import datetime db SQLAlchemy() class User(db.Model): __tablename__ user id db.Column(db.Integer, primary_keyTrue) username db.Column(db.String(64), uniqueTrue, nullableFalse, indexTrue) email db.Column(db.String(120), uniqueTrue, nullableFalse) password_hash db.Column(db.String(128), nullableFalse) avatar db.Column(db.String(256), default) role db.Column(db.String(16), defaultstudent) # student / teacher / admin created_at db.Column(db.DateTime, defaultdatetime.utcnow) def set_password(self, password): self.password_hash generate_password_hash(password) def check_password(self, password): return check_password_hash(self.password_hash, password)这里有个关键点密码一定不能明文存储。Werkzeug 的generate_password_hash默认使用 pbkdf2:sha256 算法加盐对课程网站这种场景安全性足够了。之前在项目里见过有人直接存明文密码的上线一周数据库泄露就被刷了一遍弱口令必须引以为戒。认证方案我用的是 JWT比 Flask-Login 更适合前后端分离。Flask 里用 PyJWT 实现非常简单import jwt from functools import wraps from flask import request, jsonify from datetime import datetime, timedelta SECRET_KEY your-secret-key def generate_token(user_id, role): payload { user_id: user_id, role: role, exp: datetime.utcnow() timedelta(days7) } return jwt.encode(payload, SECRET_KEY, algorithmHS256) def token_required(f): wraps(f) def wrapper(*args, **kwargs): token request.headers.get(Authorization, ) if not token.startswith(Bearer ): return jsonify({code: 401, msg: 未登录或token失效}), 401 try: payload jwt.decode(token[7:], SECRET_KEY, algorithms[HS256]) request.user_id payload[user_id] request.user_role payload[role] except jwt.ExpiredSignatureError: return jsonify({code: 401, msg: 登录已过期}), 401 except jwt.InvalidTokenError: return jsonify({code: 401, msg: 无效token}), 401 return f(*args, **kwargs) return wrapper我把这段逻辑写成了一个独立模块auth.py这样注册接口、登录接口、课程接口都能引用来做鉴权。前后端联调时每次请求只要在 Header 里带Authorization: Bearer token即可。Django 里做同样的事有 Django REST Framework djangorestframework-simplejwt 这套组合。如果项目从一开始就知道会频繁迭代权限体系选 Django 会更省心但如果只是要快速响应前端需求Flask 手动实现并不麻烦。2.2 课程、分类和评论的模型关系课程和分类是典型的树形关系一个分类下面多个课程一个课程下面多个章节一个章节下面多个视频资源。评论表单独关联到课程。我的模型如下class CourseCategory(db.Model): __tablename__ course_category id db.Column(db.Integer, primary_keyTrue) name db.Column(db.String(32), uniqueTrue, nullableFalse) sort_order db.Column(db.Integer, default0) courses db.relationship(Course, backrefcategory, lazydynamic) class Course(db.Model): __tablename__ course id db.Column(db.Integer, primary_keyTrue) title db.Column(db.String(128), nullableFalse) subtitle db.Column(db.String(256), default) cover_url db.Column(db.String(256), default) teacher_id db.Column(db.Integer, db.ForeignKey(user.id)) category_id db.Column(db.Integer, db.ForeignKey(course_category.id)) price db.Column(db.Numeric(10, 2), default0) difficulty db.Column(db.String(16), defaultbeginner) # beginner/intermediate/advanced created_at db.Column(db.DateTime, defaultdatetime.utcnow) chapters db.relationship(Chapter, backrefcourse, lazydynamic, order_byChapter.sort_order) class Chapter(db.Model): __tablename__ chapter id db.Column(db.Integer, primary_keyTrue) course_id db.Column(db.Integer, db.ForeignKey(course.id)) title db.Column(db.String(128), nullableFalse) sort_order db.Column(db.Integer, default0) video_url db.Column(db.String(512), default) duration db.Column(db.Integer, default0) # 秒为单位 is_free db.Column(db.Boolean, defaultFalse) class Comment(db.Model): __tablename__ comment id db.Column(db.Integer, primary_keyTrue) course_id db.Column(db.Integer, db.ForeignKey(course.id)) user_id db.Column(db.Integer, db.ForeignKey(user.id)) content db.Column(db.Text, nullableFalse) created_at db.Column(db.DateTime, defaultdatetime.utcnow)在设计时我特别把课程拆成了Course和Chapter两层而不是直接把视频地址挂在课程下面。为什么因为一个课程往往包含多个章节视频如果只做课程表加一个 video_url 字段后续每次增加章节都要额外建表而且章节的排序、是否免费试看这些属性没法表达。拆成两张表后课程列表接口返回课程元信息课程详情接口再返回所有章节数据量上也非常可控。评论我特意没有做回复嵌套。很多课程网站的评论实际只需要一层用户针对课程本身留言不需要复杂的多级回复。做了多级回复意味着要做递归查询、缩进渲染、删除策略全都会拖慢开发进度。这是典型的克制设计。2.3 用蓝图组织路由Flask 的蓝图模块化是组织路由的工具类似于 Django 的 app 概念。我按功能切了三个蓝图from flask import Blueprint auth_bp Blueprint(auth, __name__, url_prefix/api/auth) course_bp Blueprint(course, __name__, url_prefix/api/course) admin_bp Blueprint(admin, __name__, url_prefix/api/admin)主入口文件app.pyfrom flask import Flask from flask_cors import CORS from config import Config from models import db from api.auth import auth_bp from api.course import course_bp from api.admin import admin_bp def create_app(): app Flask(__name__) app.config.from_object(Config) CORS(app, resources{r/api/*: {origins: http://localhost:5173}}) db.init_app(app) app.register_blueprint(auth_bp) app.register_blueprint(course_bp) app.register_blueprint(admin_bp) with app.app_context(): db.create_all() return app app create_app() if __name__ __main__: app.run(host0.0.0.0, port5000, debugTrue)CORS 配置在开发阶段必须显式指定允许的前端地址。Vue 开发服务器默认跑在 5173 端口Flask 跑在 5000 端口不同端口就是跨域浏览器会拦截响应。我最初调试时前端拿不到任何数据打开浏览器控制台才看到 CORS 报错。用 Flask-CORS 大概解决了。生产环境不建议用 CORS 全开的方式把origins改成具体的域名或者前置 nginx 做反向代理由 nginx 统一配置跨域头后端代码就不用关注了。2.4 API 响应格式的统一设计前后端联调时最怕的就是每个接口返回结构不一致。有的接口返回{code: 0, data: ...}有的返回{success: true, data: ...}前端就要为每个接口单独写处理逻辑。我把所有接口返回格式统一为{ code: 0, message: ok, data: {} }错误时 code 不为 0{ code: 40001, message: 用户名或密码错误, data: null }具体接口中直接返回这个结构。做了这个约定后前端 axios 拦截器里统一处理响应业务代码只需要关心data部分代码量少了一半。Django 项目中我用 Django REST Framework 也做了同样的统一处理通过自定义 exception handler 把校验错误、权限错误、404 全部转成统一格式效果相同。3. Vue 前端与课程播放核心功能实现前端这部分我选择了 Vue 3 Vite Pinia Vue Router。为什么不选 Vue 2课程网站不是特别复杂的 SPAVue 3 的 Composition API 在处理视频播放状态、进度记录这类需要逻辑复用的场景时非常顺手而且项目是全新的没必要守着旧生态。3.1 Vite 项目创建和环境变量用 npm 创建 Vue 3 项目npm create vuelatest这个命令会交互式询问是否安装 Router、Pinia、ESLint 等按需选择即可。Vite 比 Webpack 快很多开发时修改代码热更新几乎是秒级调试体验非常关键。环境变量我拆成了三份# .env.development VITE_API_BASE_URLhttp://localhost:5000/api # .env.production VITE_API_BASE_URL/api # .env.staging VITE_API_BASE_URLhttps://staging.example.com/apiVue 代码里通过import.meta.env.VITE_API_BASE_URL获取。这个配置在部署阶段直接决定前端代码是否要重新构建。我在第一次部署时偷懒直接把开发环境地址打进生产包结果用户访问的是 localhost所有请求打到用户自己的电脑上排查了半天才意识到环境变量没切。3.2 axios 封装和路由守卫axios 前端请求拦截器必须统一加 token响应拦截器统一处理错误码。我封装了一个request.jsimport axios from axios import { useUserStore } from /stores/user import router from /router const request axios.create({ baseURL: import.meta.env.VITE_API_BASE_URL, timeout: 15000 }) request.interceptors.request.use(config { const userStore useUserStore() if (userStore.token) { config.headers.Authorization Bearer ${userStore.token} } return config }) request.interceptors.response.use( response { const res response.data if (res.code ! 0) { // 统一错误提示 return Promise.reject(new Error(res.message)) } return res.data }, error { if (error.response error.response.status 401) { // token失效跳转登录页 useUserStore().clearAuth() router.push({ name: login }) } return Promise.reject(error) } ) export default requestVue Router 的路由守卫在 Turbine 应用中很关键。未登录用户可以浏览课程列表和课程详情页但观看完整视频和发表评论需要登录。所以我在路由 meta 里标记哪些需要认证const routes [ { path: /, name: home, component: HomeView }, { path: /course/:id, name: course-detail, component: CourseDetail }, { path: /login, name: login, component: LoginView }, { path: /dashboard, name: dashboard, component: DashboardView, meta: { requiresAuth: true } } ] router.beforeEach((to, from, next) { const userStore useUserStore() if (to.meta.requiresAuth !userStore.token) { next({ name: login, query: { redirect: to.fullPath } }) } else { next() } })3.3 m3u8 视频在 Vue 中的播放课程网站的核心是视频播放这部分我踩的坑最多。最初我直接把 MP4 文件放在服务器上前端用video标签播放。很快发现问题MP4 文件太大用户没等缓冲完就关掉了而且拖进度条体验不好。最终决定用 HLS 协议把视频转成 m3u8 切片。m3u8 的本质是先把一个视频文件切分成很多小片段通常是 .ts 文件然后生成一个索引文件.m3u8播放器按索引文件顺序加载片段。好处是支持自适应码率用户网速不太好时可以自动降清不用把整个文件下载完才能播放。Vue 播放 m3u8 的推荐组合是hls.jsimport Hls from hls.js export function playM3u8(videoElement, url) { if (Hls.isSupported()) { const hls new Hls({ maxBufferLength: 30, maxMaxBufferLength: 60 }) hls.loadSource(url) hls.attachMedia(videoElement) hls.on(Hls.Events.MANIFEST_PARSED, () { videoElement.play() }) return hls } else if (videoElement.canPlayType(application/vnd.apple.mpegurl)) { // Safari 原生支持 HLS videoElement.src url videoElement.play() } }注意hls.js 在打包时体积不小建议用动态 import 实现按需加载只在视频播放页面引入避免首屏 bundle 过大。视频切片我用的是 ffmpegffmpeg -i input.mp4 -codec: h264 -acodec: aac -hls_time 10 -hls_playlist_type vod -hls_segment_filename output_%03d.ts output.m3u8参数说明-hls_time 10表示每个切片 10 秒-hls_playlist_type vod表示点播模式不变的索引文件。切片时长不要设太短或太长太短比如 2 秒会导致请求数过多太长比如 60 秒会拖动时缓冲明显实测 10 秒左右体验比较好。3.4 视频权限控制与防盗链课程网站必须控制视频访问权限免费章节允许未登录用户观看付费章节只有购买后能看。如果直接把 m3u8 地址暴露在前端代码里用户复制链接就能无限播放哪怕后端接口做了权限校验也无法阻止用户绕过前端直接请求视频文件。我用了两种方式应对第一种是简单 referer 防盗链在 nginx 层配置只允许自己的域名引用视频资源。这种方式防君子不防小人但成本最低能挡住绝大多数普通用户。第二种是给 m3u8 地址加上签名参数后端在返回章节信息时对视频地址生成一个有时效性的签名import hashlib import time def generate_signed_url(video_path): expires int(time.time()) 3600 # 1小时有效 raw f{video_path}:{expires}:{SECRET_KEY} sign hashlib.md5(raw.encode()).hexdigest() return f/video/{video_path}?expires{expires}sign{sign}服务端在视频路由中校验签名是否有效。即便别人拿到了链接一小时后自动失效有效降低资源被无限盗用的风险。3.5 前端常见问题vue 打包布局异常与样式问题开发模式下页面显示正常打包部署后布局全乱这类问题在 Vue 项目里非常常见。我遇到的有两种第一种是 CSS 的base路径问题。Vite 默认base是/如果把打包产物部署到子路径如https://example.com/course/所有静态资源请求都会指向根路径导致 CSS、JS 加载失败页面光秃秃一片。解决办法是在vite.config.js中设置base: /course/或者用相对路径base: ./确保资源路径正确。第二种是样式优先级问题常见于第三方 UI 库和自定义样式冲突。开发时热更新掩盖了加载顺序问题打包合并后 CSS 顺序变化导致样式覆盖错乱。建议给自定义样式加上更具体的选择器或使用 scoped 样式尽量不要用全局无前缀的 class 名。还有一类很容易忽略的图片资源放在public目录下打包后路径没问题放在src/assets下代码中如果没有通过 import 或 new URL 方式引用打包后会出现图片丢失。4. 后台管理基于 Django Admin 的对比体验课程网站需要后台管理我最初在 Flask 里手动实现了一套管理界面。前端页面用 Vue 写表格、表单后端提供 CRUD API。功能上没有问题代码量略多。后面我试了用 Django 快速搭建同款系统不得不承认 Django Admin 确实强大。4.1 在 Django 中创建课程 app 和模型Django 创建 app 的命令是python manage.py startapp course然后在course/models.py中定义模型和 Flask 的 SQLAlchemy 写法很像。但 Django 会自动生成数据库迁移文件python manage.py makemigrations python manage.py migrate这点对比 FlaskFlask-SQLAlchemy 的db.create_all()只能创建表结构不能修改已有表的字段而 Django 的迁移系统在字段变更时会有更完善的处理方案。如果项目迭代频繁改字段Django 的迁移机制确实比 Flask 舒服。4.2 让 Django Admin 更好看Django Admin 默认界面比较朴素如果想用于课程管理的内容运营需要稍作美化。推荐django-simpleui或django-jazzmin这类第三方主题包。我用的是django-simpleui主题看起来现代不少还自带菜单图标配置很简单主要是在settings.py的 INSTALLED_APPS 里加进去。同时把课程列表页配置为可排序、可搜索、可筛选from django.contrib import admin from .models import Course, Chapter admin.register(Course) class CourseAdmin(admin.ModelAdmin): list_display (id, title, category, price, difficulty, created_at) list_filter (category, difficulty) search_fields (title, subtitle) ordering (-created_at,) list_per_page 20这些配置让运营人员可以直接在后台管理课程分类、上下架状态、设置价格不需要前端管理页面大大降低开发成本。提示Django Admin 适合给管理员和运营人员用不适合直接暴露给普通用户。普通用户端仍然要走 Vue 前端页面通过 API 交互。4.3 Django 和 Flask 实现课程 API 的对比同一个获取课程详情的接口Flask 写法course_bp.route(/int:course_id, methods[GET]) def get_course_detail(course_id): course Course.query.get(course_id) if not course: return jsonify({code: 40400, message: 课程不存在, data: None}), 404 chapters course.chapters.all() data { id: course.id, title: course.title, subtitle: course.subtitle, chapters: [{ id: c.id, title: c.title, is_free: c.is_free, duration: c.duration } for c in chapters] } return jsonify({code: 0, message: ok, data: data})Django REST Framework 写法from rest_framework.views import APIView from rest_framework.response import Response from rest_framework import status from .models import Course from .serializers import CourseDetailSerializer class CourseDetailView(APIView): def get(self, request, course_id): try: course Course.objects.get(idcourse_id) except Course.DoesNotExist: return Response({code: 40400, message: 课程不存在}, statusstatus.HTTP_404_NOT_FOUND) serializer CourseDetailSerializer(course) return Response({code: 0, message: ok, data: serializer.data})两者核心逻辑类似。Django 用序列化器自动处理字段嵌套数据模型复杂时省不少代码Flask 用列表推导手动构造字典简单场景更灵活。如果课程嵌套层级越来越多分类、标签、讲师信息、章节、测评题Django 的序列化器优势会更明显。5. 部署上线与常见问题排查本地开发跑通和真正上线差距不小这个项目部署到 Linux 服务器上的过程我也完整记录下来。部署方式选择了宝塔面板 Gunicorn Nginx先讲 Flask 版。5.1 使用 Gunicorn 启动 Flask 服务在 Linux 下不建议用 Flask 自带的开发服务器跑生产性能和安全都不过关。我用的 Gunicornpip install gunicorn gunicorn -w 4 -b 127.0.0.1:5000 app:app参数说明-w 4表示 4 个 worker 进程worker 数一般设置为 CPU 核心数的 2 到 4 倍。课程网站的接口以 IO 操作为主4 个 worker 对 2 核 4G 服务器足够。app:app中前者是模块名app.py后者是 Flask 实例。5.2 宝塔面板部署 Django 时注意的坑Django 部署和 Flask 最大的区别是需要处理静态文件和 ALLOWED_HOSTS。使用宝塔部署 Django 时先在宝塔把 Python 项目创建好选择 Python 3.10 和对应的框架类型安装依赖pip install -r requirements.txt执行python manage.py collectstatic收集静态文件配置 Nginx 反向代理到 Gunicorn 或 uWSGIDjango 的ALLOWED_HOSTS必须填上你的域名或 IP否则访问直接报 DisallowedHost 错误ALLOWED_HOSTS [yourdomain.com, 你的服务器IP]还有个坑是 MySQL 驱动问题。Django 连 MySQL 通常用mysqlclient在 Linux 上安装前需要先装依赖yum install mysql-devel gcc gcc-c python3-devel pip install mysqlclient如果在纯净系统上直接pip install mysqlclient大概率报编译错误。也可以换用pymysql在settings.py里加上import pymysql pymysql.install_as_MySQLdb()两者的区别是mysqlclient是 C 扩展性能更好pymysql是纯 Python 实现安装省事但性能稍弱。中小型课程网站用 pymysql 完全足够。5.3 Nginx 配置前端静态资源和 API 反向代理前端打包后生成dist目录把里面的文件上传到服务器/var/www/course-web然后 Nginx 配置server { listen 80; server_name yourdomain.com; # 前端静态资源 root /var/www/course-web; index index.html; # Vue Router history 模式需要配置 try_files location / { try_files $uri $uri/ /index.html; } # API 反向代理到 Flask/Django location /api/ { proxy_pass http://127.0.0.1:5000; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; } # 视频文件缓存 location /video/ { proxy_pass http://127.0.0.1:5000; proxy_cache_valid 200 1h; } }这里的try_files $uri $uri/ /index.html;非常关键。Vue Router 启用了 history 模式后前端路由如/course/1是虚拟路径后端没有真实目录。如果少了这行用户刷新课程详情页会变成 404。5.4 后端开发中容易踩的隐藏问题开发过程中很多坑是隐藏的只在特定条件下爆发。我把遇到过的列成速查表问题现象原因解决办法数据库乱码中文显示为问号MySQL 表字符集不是 utf8mb4建库时指定DEFAULT CHARSETutf8mb4时区差 8 小时课程发布时间差了整整 8 小时MySQL 和 Python 时区不一致设置app.config[TIMEZONE] Asia/Shanghai数据库中存 UTC展示层转本地上传图片 413Nginx 拒绝大文件Nginx 默认 body 大小 1M在 Nginx 配置加client_max_body_size 20M;跨域请求被拦截浏览器 console 报 CORS 错误后端没有设置允许的前端域名Flask 用 Flask-CORSDjango 用 django-cors-headersJWT 过期后静默失败用户点课程没反应前端没有捕获 401 状态在 axios 响应拦截器里统一跳登录页视频播放进度丢失刷新页面从头播放没有记录用户观看进度前端定时上报当前播放时间后端记录到数据库Django 部署到宝塔后还有个常见坑耗时接口超时。Gunicorn 默认timeout 30秒如果导入视频转码或发送邮件这类耗时超过 30 秒的接口会被强制杀掉。需要在启动命令加参数gunicorn -w 4 -b 127.0.0.1:5000 --timeout 120 app:app5.5 数据库类型转换与查询优化后端开发中 Python 类型和数据库类型的转换也需要留心。Flask-SQLAlchemy 里常见场景前端传price字段是字符串存到Numeric(10,2)类型的数据库列中MySQL 会自动转换但如果传了空字符串MySQL 会报错。所以接口里入参校验必须做不能依赖数据库容错。课程列表页的查询也容易写慢特别是嵌套关系多时。我用 Flask-SQLAlchemy 时的优化思路# 错误写法每个循环都查一次数据库 courses Course.query.all() for c in courses: print(c.category.name) # N1查询问题 # 正确写法用 joinedload 一次性关联查询 from sqlalchemy.orm import joinedload courses Course.query.options(joinedload(Course.category)).all()Django 里对应的优化是select_related和prefetch_relatedcourses Course.objects.select_related(category).all()这类问题在数据量少的时候看不出差别一旦课程数超过几百性能差距会很明显。这也是我在做这个项目时最有体感的一个优化点。6. 项目扩展和我的体会做完这个课程网站我最大的感受是技术选型的核心不是追求最新最热而是匹配自己团队和项目的实际需求。Flask 的优势是轻量、灵活、掌控感强适合中小规模项目和想要把每个环节都搞清楚的学习者Django 的优势是全家桶式的一体化解决方案尤其在后台管理、权限系统、安全防护这些方面开箱即用。两者没有绝对的高下之分关键在于你愿意花多少时间在基础设施上。如果你从零开始做类似项目我的路径建议是第 1 周先确定需求画出页面原型明确数据模型。这一步省不了数据模型设计的好坏直接决定后续改接口的工作量第 2 周后端 API 开发。用 Flask 或 Django 把用户、课程、评论的核心接口跑通用 Postman 或 Apifox 测好第 3 周前端页面开发。Vue 这边重点是组件划分和路由设计视频播放单独一个组件方便复用和测试第 4 周前后端联调和部署。这部分预留的时间建议多一点跨域、权限、打包、服务器环境各种细节问题都是在联调阶段暴露出来的最后再分享一个实用小技巧视频切片文件如果很多建议放在单独的磁盘目录或者对象存储中不要塞进项目目录不然每次部署拉代码会非常痛苦。我就因为视频文件直接放在 media 目录下导致 git 仓库体积膨胀到几个 G后面不得不清理历史提交记录才解决。这个项目的完整代码我已经整理好了不过这篇文章已经很长了就不贴完整源码了。如果你在搭建过程中遇到具体报错欢迎在评论区留言我会尽量帮你看。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价