尧图网络科技YAOTU DIGITAL 获取报价
获取报价
首页 / 资讯中心 / 文章详情

从Django到Vue:手把手实现民俗文化系统全栈开发与部署

发布时间:2026/9/24 18:39:50

资讯中心
01
ARTICLE

从Django到Vue:手把手实现民俗文化系统全栈开发与部署

从Django到Vue:手把手实现民俗文化系统全栈开发与部署
做民俗文化系统这个项目其实一开始并不是为了追什么技术热点而是老家那边搞非遗保护的朋友找过来想让本地的民俗活动、传统手艺、老物件这些东西有个能看、能查、能维护的线上阵地。当时我脑子里第一反应就是Python Vue这套组合后端在Django和Flask之间纠结了一下最后用Django为主Flask留了个快速原型做演示。整体做下来从环境搭建到上线部署踩了不少坑也攒了不少可以直接拿走的经验。这篇就把整个系统从零到一的过程拆开讲清楚包括技术选型、表结构设计、前后端联调、m3u8视频播放、生产部署这些问题适合刚准备做类似文化展示类Web项目的朋友参考也适合做毕设、课设的同学直接抄作业。1. 项目整体设计与技术选型思路1.1 民俗文化系统的真实需求先说需求。很多人一听“民俗文化系统”就觉得是个纯展示网站做个轮播图加几个详情页就完事了实际上真正做起来根本不是那么回事。我这边整理下来的核心需求有这么几类一是民俗项目的图文展示包括项目名称、所属地区、级别、传承人、历史渊源这些信息二是多媒体资源的承载图片、音频、视频都得能传能播三是后台管理总不能每改一条介绍都让开发重新部署四是检索和分类用户要能按地区、按类型筛选。如果后面扩展还要考虑数据采集爬虫的接入——热词里也提到了Python爬虫很多民俗资料其实散在各个地方爬过来做数据填充是很常见的操作。这个定位决定了系统不能选择纯静态页方案也不能做得太重。纯静态页没法解决后台维护问题一上来就搞微服务又完全没必要单体应用加一个前端框架是最务实的路径。所以我最终定的整体架构是Django提供REST API和管理后台Vue负责前端页面展示两者通过HTTP接口通信数据库用SQLite起步、上线切MySQL部署用waitress nginx。这套组合在近几年个人项目里非常常见选它不是因为“大家都这么干”而是它刚好卡在成本和效率的最佳点上。1.2 后端为什么选PythonDjango与Flask怎么选标题把django和flask都列出来了这俩我都有实际项目经验直接讲结论。Flask的优势是轻、灵活一个文件就能跑起来特别适合快速搭个原型或者接口量很小的工具项目但它的短板是“自由过了头”ORM、表单验证、Admin后台、迁移工具都要自己选型、自己组装项目一复杂决策成本就上去了。Django则是“全家桶”自带ORM、Admin、迁移、认证、分页这些开箱即用的东西民俗系统这种典型CRUD偏重的项目用Django开发效率高得不是一点半点。为了说得更直观我把两个框架在项目里的对比整理了一下对比维度DjangoFlask开发效率高脚手架齐全前期快后期要自己补组件ORM自带好用支持复杂查询默认没有要配SQLAlchemyAdmin后台自带改model就出界面需要扩展包实现适合场景数据管理型、内容型系统接口服务、小工具、原型验证学习曲线略陡但体系完整平缓自由发挥空间大实际开发中我是两个都用了Django作为主项目Flask写了一个数据采集接口的快速验证后面爬虫抓回来的数据先在Flask里跑通清洗流程再落库给Django用。这样做的好处是验证阶段不受Django项目结构拖累代码几十行就能跑确认可行之后再挪进正式项目。如果你只打算做单个系统我建议直接选Django别在Flask上纠结太久后面所有坑我都能帮你绕开。1.3 前端为什么选Vue从模板渲染到前后端分离早期Django开发是服务端渲染那一套也就是Django的Template View页面由后端拼好再返回给浏览器。对于民俗系统来说纯模板渲染也不是不能用但问题在于交互复杂起来之后——视频播放状态切换、多条件筛选、图片懒加载、组件复用——模板语言会很吃力前端代码会变成一堆堆不起来的script片段。所以这次我选择前端用Vue做完整的单页应用后端只负责出JSON数据页面渲染和交互全交给前端。Vue的上手难度在前端三大框架里是最友好的它的核心是组件化一个页面可以拆成导航组件、卡片组件、轮播组件、播放器组件每个组件管自己的HTML、CSS和逻辑互不干扰。热词里提到vue路由传参、vue样式、vue v-for这一类问题都是组件开发里的高频场景后面实操部分我会详细展示怎么用。另外Vue对于播放m3u8这种流媒体格式也有成熟的生态方案这点对民俗视频展示非常关键毕竟很多融媒体素材是m3u8切片流不是普通mp4。1.4 Django的MTV模式应该怎么理解热词里有“django之mtv模式的mtv有什么作用”这个是真有人问因为很多人一上来就被MVC和MTV搞晕。Django官方设计是MTV模式M是Model对应数据表T是Template对应页面模板V是View对应业务逻辑。和传统MVC最大的区别在于Django把“控制器”的职责交给了框架内部的URLConf和View函数共同承担开发者的核心工作变成定义数据模型、编写视图函数、配置路由。理解MTV的关键在于不要死记英文缩写而要理解数据流用户在浏览器输入URL请求路由到ViewView去Model里查数据然后把数据交给Template渲染成HTML返回前端。如果做前后端分离Template这一层就弱化甚至不用了View直接返回JSON这项工作由Django REST framework来完成。也就是说理解MTV是理解Django一切操作的地基后面讲ORM增删改查、接口设计都会围绕这个数据流来展开。2. 环境搭建与开发工具链准备2.1 Python环境和虚拟环境很多新手在Python安装那一步就卡住了。我的建议是Windows下直接去官网下载Python 3.10或3.11的安装包装的时候一定勾选“Add Python to PATH”这个勾不选的话后面在命令行里敲python会提示不是内部命令。装完打开cmd输入python --version能输出版本号就说明成功了。这里有个容易踩的坑如果你电脑上装了Microsoft Store版本或者多个Python版本命令行里出现的可能是另一个解释器建议统一用py -3 -m pip这种写法指定版本。虚拟环境是这一步最容易跳过但最不该跳过的环节。不用虚拟环境的后果是不同项目的依赖包版本互相打架比如一个项目要Django 3.2另一个要Django 4.2全局安装会直接冲突。项目目录下建虚拟环境的操作很固定python -m venv venv venv\Scripts\activate激活之后命令行前面会出现(venv)前缀后面所有用pip安装的包都会装进这个环境跟系统隔离。等把项目代码提交到Git的时候记得把venv目录加进.gitignore不然一堆本地依赖文件全被提交上去别人拉代码会很痛苦。2.2 Pycharm配置与激活问题Pycharm是Python开发的主流IDE热词里关于pycharm激活、pycharm激活码的问题特别多。这里我直接说明一下立场商业软件请通过正规途径获取授权网上流传的激活码、补丁方式既违反软件许可协议也有安全风险这里不展开也不推荐。实际上Pycharm社区版对Django开发已经非常够用了。新建项目的时候直接选Django模板它会帮你把项目骨架建好并自动配置虚拟环境。唯一要做的关键动作是在Settings - Project - Python Interpreter里把解释器指向venv目录下的python.exe这样编辑器才会识别你装的Django、DRF这些包代码补全和语法检查才能生效。如果打开项目后import Django一直飘红9成是解释器没配好先别怀疑代码去检查这里。Pycharm里调试Django也很顺手直接配置一个Django Server运行配置填上host和port就能像调试普通Python脚本一样在视图函数里打断点。这个效率比用命令行跑服务器加print调试高特别多我后来排查接口数据异常几乎全靠打断点。2.3 Django项目初始化与创建App项目初始化这块我给出一个能直接照着敲的命令序列。以windows环境为例在虚拟环境激活状态下django-admin startproject folk_culture cd folk_culture python manage.py startapp items python manage.py startapp users python manage.py migrate python manage.py runserver执行完打开http://127.0.0.1:8000/看到Django火箭成功页面就说明骨架没问题了。这里有两个建议第一项目配置文件的名字不要太随意folk_culture这种一眼能看出业务含义的就好不要用test、demo这类名字后面部署和模块导入都会用到第二App拆分按模块来items放民俗项目、users放用户、activities可以放活动通知不要所有model都堆在一个app里代码一多根本没法维护。migrate这个命令是新手容易忽略的它的作用是把Django内置那些model比如用户表、session表建到数据库里。如果不执行直接用admin或登录功能会报“no such table”的错误。后面每新增一个model都要先makemigrations再migrate这个组合操作会伴随你整个开发周期。2.4 Vue环境搭建与依赖安装前端环境相对麻烦一点因为要先装Node.js。在Node官网下载LTS版本的安装包一路默认安装装完在命令行验证node -v和npm -v。然后创建Vue项目我推荐用Vite而不是老一代的Vue CLI因为Vite启动速度快很多配置也更简洁。如果之前不熟悉Vite下面这套命令可以直接用npm create vitelatest folk-web -- --template vue cd folk-web npm install npm run devnpm install这一步在部分网络环境下会比较慢会卡在fetchMetadata阶段热词里提到的“vue安装依赖”问题很多就是这个原因。解决办法是切换到国内npm镜像源命令是npm config set registry https://registry.npmmirror.com设置完再跑一次install会快非常多。这不算什么黑科技就是平时开发都会做的基础优化。装完基础依赖后需要额外安装vue-router、axios如果做m3u8视频播放还要装视频相关库命令如下npm install vue-router4 axios npm install hls.jsvue-router负责前端路由跳转axios负责和Django后端交互hls.js处理m3u8视频流。这三个库后面每个都会用到最好一开始就装好。前端项目创建完成后把Vite配置文件里的server.proxy配一下开发阶段就能把/api开头的请求代理到Django地址避免每次都要写完整后端的URL。3. 后端核心功能实现民俗数据从建模到接口3.1 表结构设计民俗系统的数据模型是整个项目的根基设计得好不好直接决定后面开发顺不顺畅。我在items这个app里设计了四张表FolkItem存储民俗项目本身Field是传承人Category是分类MediaResource存图片和视频。字段的设计遵循“宁精勿滥”的原则只保留真正会用的信息不为了炫技加一堆没用的字段。参考下面这个核心Model的设计from django.db import models class Category(models.Model): name models.CharField(max_length50, verbose_name分类名, uniqueTrue) parent models.ForeignKey(self, nullTrue, blankTrue, on_deletemodels.CASCADE, verbose_name父分类) class Meta: verbose_name 分类 verbose_name_plural 分类 def __str__(self): return self.name class FolkItem(models.Model): name models.CharField(max_length100, verbose_name项目名称) category models.ForeignKey(Category, on_deletemodels.PROTECT, verbose_name所属分类) region models.CharField(max_length100, verbose_name所在地区) level models.CharField(max_length20, verbose_name级别, choices[ (national, 国家级), (provincial, 省级), (city, 市级), (county, 县级) ], defaultcounty) content models.TextField(verbose_name详细介绍, blankTrue, default) inheritor models.CharField(max_length50, verbose_name传承人, blankTrue, default) created_at models.DateTimeField(auto_now_addTrue, verbose_name创建时间) updated_at models.DateTimeField(auto_nowTrue, verbose_name更新时间) class Meta: ordering [-updated_at] verbose_name 民俗项目 verbose_name_plural 民俗项目 def __str__(self): return self.name class MediaResource(models.Model): title models.CharField(max_length100, verbose_name资源标题) item models.ForeignKey(FolkItem, on_deletemodels.CASCADE, related_namemedia, verbose_name关联项目) media_type models.CharField(max_length20, verbose_name资源类型, choices[ (image, 图片), (audio, 音频), (video, 视频) ], defaultimage) file models.FileField(upload_tomedia/%Y/%m/, verbose_name文件) video_url models.URLField(blankTrue, verbose_namem3u8视频地址, default)几个设计要点单独说一下。分类表用自关联外键实现树形结构这样以后要支持“传统技艺”下面再分“刺绣”“编织”这种二级分类就不用改表结构。FolkItem的category外键用on_deletemodels.PROTECT意思是如果一个分类下已经有项目直接删分类会被数据库拒绝避免误删导致数据被连带清空而MediaResource的item外键用CASCADE项目删除时多媒体资源跟着删这符合业务逻辑——项目没了资源留着的意义也不大。等实际开发中真正触发这些约束的时候你就知道当初设计这个细节能救命了。3.2 Django ORM的增删改查与删除对象的细节热词里有一条“django执行查询-删除对象”这确实是新手容易搞迷糊的地方。Django的ORM删除操作分为两种一种是模型实例的delete()一种是查询集的批量delete()。举个实际例子# 方式一删除单个对象 item FolkItem.objects.get(id3) item.delete() # 方式二删除查询集内所有对象返回(总删除数, 各表删除数) result FolkItem.objects.filter(levelcity).delete() print(result) # (2, {items.FolkItem: 2})这里最容易出问题的是关联删除。因为MediaResource的外键用了CASCADE所以删除FolkItem时关联的图片和视频资源会自动删除但如果这里用了PROTECT就会抛出ProtectedError异常。项目里一定要搞清楚每个外键的on_delete策略删除之前心里要有数。有一个常用的技巧是在删除前先统计关联数量# 删除前先查询关联资源数量 if item.media.count() 0: print(f该项目下有{item.media.count()}个媒体资源将被一并删除)查询那块Django ORM的链式查询是很方便的比如按地区和级别筛选民俗项目items FolkItem.objects.filter(region__contains福建, levelnational).select_related(category)select_related这个优化很多人不用但访问外键关联对象时有它可以把SQL从多次查询优化成一次join数据量大的时候性能差距很明显。接口返回时再配合序列化器前端拿到的就是结构化的JSON了。3.3 序列化与接口设计、跨域处理前后端分离的关键在后端接口。Django REST framework是这一层的主角它能把你定义的Model自动转成JSON。打开settings.py在INSTALLED_APPS里加上rest_framework然后创建序列化器from rest_framework import serializers from .models import FolkItem, MediaResource class MediaSerializer(serializers.ModelSerializer): class Meta: model MediaResource fields [id, title, media_type, file, video_url] class FolkItemSerializer(serializers.ModelSerializer): category_name serializers.CharField(sourcecategory.name, read_onlyTrue) media MediaSerializer(manyTrue, read_onlyTrue) class Meta: model FolkItem fields [id, name, category, category_name, region, level, content, inheritor, media, updated_at]视图层用ViewSet加路由一整套标准接口就出来了from rest_framework import viewsets from rest_framework.decorators import action from rest_framework.response import Response from .models import FolkItem from .serializers import FolkItemSerializer class FolkItemViewSet(viewsets.ModelViewSet): queryset FolkItem.objects.all().select_related(category).prefetch_related(media) serializer_class FolkItemSerializer action(detailFalse, methods[get]) def featured(self, request): 首页推荐项目 featured_items self.get_queryset()[:6] serializer self.get_serializer(featured_items, manyTrue) return Response(serializer.data)这里有个API设计的习惯问题不要把整个列表一次性返回而是支持分页。DRF默认支持分页配置一下PAGE_SIZE前端请求时用?page1page_size10控制数据量接口压力会小很多。跨域问题也是必须解决的。Django默认不允许别的域名请求接口前端在localhost:5173后端在localhost:8000不同端口也算跨域。处理方式是用django-cors-headers# 安装后settings.py配置 INSTALLED_APPS [ # ... corsheaders, ] MIDDLEWARE [ # ... corsheaders.middleware.CorsMiddleware, # ... ] CORS_ALLOWED_ORIGINS [ http://localhost:5173, ]配置完这个Vue里用axios请求接口就不会有跨域报错了。这一步是前后端联调的基础很多人联调不成功一半的case就是跨域拦截。3.4 如果用Flask怎么做模板绑定与路由传参虽然我主项目选了Django但Flask这一块也做过快速原型热词里两条问题——“flask如何绑定到网页元素”和“flask web开发实战”——说明确实有不少人是从Flask入门的。Flask把数据绑定到网页元素的核心是利用Jinja2模板引擎。下面这个例子展示了后端变量如何传到前端HTMLfrom flask import Flask, render_template app Flask(__name__) app.route(/item/int:item_id) def item_detail(item_id): # 假设从数据库查出来的民俗项目 item { name: 木偶戏, region: 福建泉州, content: 传统戏剧类非遗项目... } return render_template(item_detail.html, itemitem)对应的HTML模板这样写h1{{ item.name }}/h1 p{{ item.region }}/p div{{ item.content }}/div这就是“绑定网页元素”的本质——Jinja2在服务端把字典或对象的值替换进模板的占位符然后返回完整的HTML给浏览器。理解了这个就理解了模板渲染和前后端分离的核心差异一个是在服务器拼HTML一个是在浏览器端拿JSON再自己渲染。Flask做小型演示系统确实快代码量比Django少很多但做到后台管理、用户权限这些功能时就会体会到插件的割裂感。所以我的建议还是那句话要做完整系统选Django只想快速出demoFlask完全够用。3.5 Admin后台、图片上传与多媒体资源管理Django自带的Admin后台是这个框架最吸引人的地方之一等于白送了一个管理界面。把FolkItem和MediaResource注册进去后在items/admin.py写这样几行from django.contrib import admin from .models import Category, FolkItem, MediaResource admin.register(FolkItem) class FolkItemAdmin(admin.ModelAdmin): list_display (name, category, region, level, updated_at) list_filter (level, category) search_fields (name, region) ordering (-updated_at,) admin.site.register(Category) admin.site.register(MediaResource)admin后台就支持按级别和分类筛选、按名称和地区搜索、排序展示真正做到了“改model就出界面”。后期民俗数据录入完全不用再单独开发一套后台页面让运营同事直接用admin管理数据就行。上传文件这块要注意配置MEDIA相关设置# settings.py MEDIA_URL /media/ MEDIA_ROOT BASE_DIR / media然后在主urls.py里做开发环境的静态文件映射不然上传的图片和视频在页面上访问不到。这个配置我在第五部分会专门讲排查流程。4. 前端Vue核心页面与多媒体展示实现4.1 组件化架构与路由规划Vue前端这部分我按页面功能拆成了这样几个组件和页面首页HomePage.vue展示推荐项目和轮播列表页ItemList.vue负责分类和筛选详情页ItemDetail.vue展示项目完整信息后台管理页AdminPage.vue对接数据维护。路由配置在router/index.js里采用懒加载写法让首屏只加载当前页面的代码速度会好一些import { createRouter, createWebHistory } from vue-router const routes [ { path: /, name: Home, component: () import(../views/HomePage.vue) }, { path: /items, name: ItemList, component: () import(../views/ItemList.vue) }, { path: /item/:id, name: ItemDetail, component: () import(../views/ItemDetail.vue), props: true } ] const router createRouter({ history: createWebHistory(), routes }) export default router这里用到了热词里的“vue路由参数”:id就是路由参数详情页可以通过route.params.id拿到项目ID。组件化的意义在于像“卡片列表”这种在首页和列表页都会出现的UI抽成一个FolkCard.vue组件两处直接FolkCard :itemitem /复用改样式只改一处效率和可维护性都高很多。4.2 axios对接Django接口的写法axios封装这一步很多人做得不够好每个页面都写一遍baseURL、拦截器既啰嗦又难维护。我习惯建一个api.js统一管理接口请求和拦截器import axios from axios const request axios.create({ baseURL: http://127.0.0.1:8000/api, timeout: 10000 }) // 请求拦截器 request.interceptors.request.use(config { const token localStorage.getItem(token) if (token) { config.headers.Authorization Token ${token} } return config }) // 响应拦截器统一处理错误 request.interceptors.response.use( response response.data, error { if (error.response error.response.status 401) { // 登录失效处理 } return Promise.reject(error) } ) export default request页面里调用就非常简洁了。以列表页为例import request from ../api async function getItems(params) { const data await request.get(/items/, { params }) return data }这里要注意一个细节Django项目里有CSRF防护前后端分离时登录状态如果走Session认证请求时就要带上CSRF Token。我这边项目直接用DRF的TokenAuthentication登录后拿到token存在localStorage请求头带Authorization: Token xxx绕开了CSRF的坑也符合分开部署的场景。如果只是展示系统不涉及登录接口直接公开读权限即可但admin后台和写操作一定要做权限控制。4.3 活动视频与m3u8视频在Vue里的播放方案热词里“vue播放m3u8”“vue播放欢乐谷m.3u8”这类问题的核心诉求其实是怎么在浏览器里播放HLS流媒体格式。m3u8不是一个完整的视频文件而是一个播放列表索引里面指向一组ts切片文件。原生浏览器video标签不支持m3u8所以必须借助hls.js在JavaScript层面做解封装和转码。我的推荐方案是封装一个VideoPlayer组件内部根据src后缀自动选择播放策略源码可以直接参考template video refvideoRef controls classvideo-player/video /template script setup import { ref, onMounted, watch, onBeforeUnmount } from vue import Hls from hls.js const props defineProps({ src: { type: String, required: true } }) const videoRef ref(null) let hls null function initPlayer(src) { if (!videoRef.value) return const video videoRef.value // 清理旧实例 if (hls) { hls.destroy() hls null } const isM3u8 src.includes(.m3u8) if (isM3u8 Hls.isSupported()) { hls new Hls({ maxBufferLength: 30, lowLatencyMode: true }) hls.loadSource(src) hls.attachMedia(video) hls.on(Hls.Events.MANIFEST_PARSED, () { video.play().catch(() { /* 自动播放被拦截忽略 */ }) }) } else { video.src src } } onMounted(() { if (props.src) initPlayer(props.src) }) watch(() props.src, (val) { if (val) initPlayer(val) }) onBeforeUnmount(() { if (hls) hls.destroy() }) /script组件使用的方法VideoPlayer srchttps://example.com/media/live/2024/carnival.m3u8 /几个关键参数说明maxBufferLength控制最大缓冲长度本地视频建议小一点移动端别太大否则切后台回来会有一大段黑屏lowLatencyMode适合直播类场景可以降低延迟但普通点播建议关闭以省带宽。hls.js在Safari浏览器里其实用不着因为Safari原生支持HLS直接给video标签赋src就行Hls.isSupported()的判断会自动处理这种情况。注意m3u8播放的场景经常涉及视频源跨域。如果m3u8地址和前端不在同一域名需要视频服务器返回正确的Access-Control-Allow-Origin头hls.js才能加载ts切片否则会出现“网络请求失败”或者“媒体加载失败”这类报错。实在控制不了视频源的话可以在视频服务器前面套一层代理转发或者让运维在nginx配好跨域头。5. 常见问题与排查技巧实录5.1 Django static文件显示不了的排查流程“在vscode写img标签在django的static文件中显示不了”这个热词能排进Django新手问题前三。我在项目里也遇到过排查步骤我总结成一条固定流程第一步确认settings.py里有没有STATIC_URL /static/和STATICFILES_DIRS [BASE_DIR / static]第二步确认模板里加载静态文件用的是{% load static %}加{% static images/xxx.png %}不要用绝对路径硬编码第三步确认static目录下文件确实存在文件名大小写要和代码里一致Windows文件系统不区分大小写但Linux服务器区分第四步开发环境跑runserver时Django默认能处理static文件如果配置了Nginx就检查Nginx的location /static映射。另外用户上传的图片属于media文件不归static管理。前端页面访问上传图片的URL要用MEDIA_URL而不是STATIC_URL。好多人把这两个搞混图片一直显示404其实路径就找错了。5.2 前端路由刷新404Vue单页应用用history模式时刷新/items/3这个地址Django会去路由表里找路径找不到就返回404这是Vue应用部署后极常见的坑。解决方案在服务端开发时用Vite的dev server能把所有请求重写回index.html生产环境用nginx配置一个try_files规则把不存在路径指回前端入口location / { try_files $uri $uri/ /index.html; }如果你是部署在Django里作为静态站点托管也可以在Django的urls.py末尾加一个兜底路由做相同的效果。这个坑也困扰了我很久后面掌握了原因之后每次部署都会在前端构建后先测一遍刷新。5.3 m3u8黑屏/不播放的排查用hls.js播放m3u8最常见的五种情况一是hls.js没安装成功控制台报Hls is not defined检查依赖是否装上二是配置了HTTPS但视频源是HTTP浏览器默认会阻止混合内容要么把页面降级要么让视频源支持HTTPS三是视频源不是标准的HLS流比如只是把mp4改名为m3u8后缀这种就需要服务端真正的切片工具来转换四是ts切片顺序有问题播放卡顿或跳帧五是CDN缓存同一个URL反复拉旧切片在hls.js配置里加Cache-Control: no-cache或者给URL加时间戳参数。排查办法是在hls实例上监听错误事件hls.on(Hls.Events.ERROR, (event, data) { console.log(HLS错误:, data.type, data.fatal) if (data.fatal) { switch (data.type) { case Hls.ErrorTypes.NETWORK_ERROR: hls.startLoad() break case Hls.ErrorTypes.MEDIA_ERROR: hls.recoverMediaError() break } } })这个监听代码能自动恢复一些非致命错误比如网络抖动导致的加载失败用户体验会好很多。5.4 删除记录时外键报错的解决删数据时遇到ProtectedError报错信息大致是“Cannot delete some instances of model Category because they are referenced through protected foreign keys”。这个就对应我之前设计Category外键用PROTECT的策略。解决办法不是去掉PROTECT而是先处理好引用数据# 先把这个分类下的民俗项目迁移到其他分类 FolkItem.objects.filter(categoryold_category).update(categoryfallback_category) # 再删除分类 old_category.delete()整个过程用事务包住防止中途报错数据不一致。5.5 生产部署waitress nginxWindows环境下部署Django服务热词里提到waitress nginx这个组合这也是我实际用的方案。waitress是一个纯Python的WSGI服务器Windows上比gunicorn好用gunicorn在Windows下支持很差命令很简单pip install waitress waitress-serve --listen127.0.0.1:8000 folk_culture.wsgi:application然后nginx负责对外提供HTTP服务、静态文件和反向代理。nginx配置参考server { listen 80; server_name www.example.com; location /static/ { alias D:/folk_culture/static/; } location /media/ { alias D:/folk_culture/media/; } location / { proxy_pass http://127.0.0.1:8000; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; } }部署后容易遇到的问题admin后台样式丢失通常是Django没把静态文件收集到nginx的alias目录需要先跑python manage.py collectstatic还有上传的图片无法访问同样要确认media目录的alias路径是否正确。生产环境没必要把媒体文件全堆在Django服务器上条件允许的话把文件丢到对象存储访问地址换成CDN域名稳定性会好很多不过后续再加入这个改造成本也不高。其他部署形式我也试过Linux上可以用gunicorn nginx方案本质一样就是WSGI服务器换成gunicorn云服务器上直接上Docker Compose编排Django PostgreSQL nginx三个容器适合后续要扩展各种服务时再用对于这个项目规模来说属于过度设计暂时不需要。6. 横着走一遍踩坑与迭代经验6.1 热门开发问题速查表把整个项目过程中遇到的典型问题整理成一个速查表开发时候遇到可以直接查省得反复搜索问题现象可能原因检查要点pip安装包慢/超时默认源慢切换国内镜像python不是内部或外部命令安装时没勾选Add to PATH手动添加环境变量Django启动报端口被占用8000被占用换端口或杀进程前端请求接口报跨域后端没配CORS检查corsheaders配置刷新页面404history模式没配置nginx加try_files上传图片不显示media路径没映射检查MEDIA_ROOT/URL删除分类报ProtectedErrorPROTECT外键保护先调整引用数据m3u8播放失败跨域/源格式错误看hls.js错误事件这些坑不是看一遍文档就能避开的基本都是实际跑项目跑出来问题才出现在这个表里的。建议你开发时也以这个方式记录问题后面交付、汇报项目时会显得特别有经验。6.2 数据可视化与爬虫扩展方向热词里还出现了“python爬虫可视化界面”。民俗文化系统做完整版大概率要涉及数据采集和数据分析。民俗资料分散在地方志、新闻网站、非遗名录几个地方靠人工录入效率很低用Python爬虫抓取是合理的扩展。但有个前提要提醒一下爬虫抓过来的数据必须做清洗和去重否则各种格式混乱的字段会把后台管理折磨疯。清洗这一步我推荐用pandas拿到DataFrame后统一处理地区名称、空值、重复项干净了再入库。数据可视化是系统亮点用ECharts配Vue地图展示各地非遗项目分布、柱状图统计各分类数量视觉效果和说服力都很不错。ECharts生态成熟文档全Vue里用起来比手写Canvas省太多事。具体实现就是在组件里引入echarts初始化容器塞入option配置。如果项目展示需要这个功能建议添加投入产出比很高。6.3 项目后续扩展建议这个项目完成之后还可以做几个很有价值的扩展方向。第一个是加会员和评论功能让用户能对非遗项目发表评价和互动涉及用户表、评论表和权限控制第二个是上线移动端适配Vue项目做响应式还比较顺手把布局文件拆成不同断点第三个是数据统计后台给管理人员展示访问量和分类热度方便做内容运营决策。我个人的经验是做这类系统时最忌讳一上来就把所有功能都规划好、写大而全的设计文档。应该先做核心展示链路——数据入库、接口输出、页面渲染打通之后再一个个加边缘功能。这样能保证每个阶段都有可演示的成果对个人项目、毕业设计都很重要。项目代码上传到Git仓库时记得写清楚README把启动步骤和依赖列明白不然过两个月自己都忘了怎么跑起来更别说别人帮你看代码了。做一个民俗文化系统技术上的难度其实不算大真正的难度在于对业务的理解和对细节的坚持。比如民俗项目数据录到一半发现有些字段得改比以前更隐蔽的问题是初期没多想就加了新需求然后发现之前的表结构设计不够适配。踩过几次坑之后我现在的习惯是开发前把数据模型来回想清楚宁可多花两天设计也不要后期加班改表。希望这篇文章里的架构决策、完整代码、排查方法能帮你少走一点弯路。
02
RELATED NEWS

相关资讯

更多网站建设与数字化升级内容

03
WHY YAOTU

想打造同款高转化官网?

懂行业、懂生意,从建站到增长一站式陪跑

场景化定制

不做模板站,围绕你的业务场景量身设计,小众不撞款。

营销型架构

以转化目标组织内容与路径,让官网真正带来询盘。

全周期服务

设计、开发、运营、运维一体,上线只是开始。

免费获取你的建站方案

留下需求,专属顾问 24 小时内为你输出方案建议。