ARTICLE DETAIL

资讯详情

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

Python+Vue全栈音乐网站实战:Django与Flask双后端架构

Python+Vue全栈音乐网站实战:Django与Flask双后端架构 用Python和Vue写一个音乐网站这个题我前前后后折腾了两周多。项目标题里提到“酷听音乐”本质就是一个基于Django/Flask做后端、Vue做前端的全栈Web应用。很多人第一次看到“Django和Flask同时出现”会觉得奇怪其实这个组合非常常见Django负责主站的核心业务和后台管理Flask单独拆出来做爬虫或者推荐这种轻量服务两边共用同一套数据库。这篇就把我当时从环境搭建到前后端联调再到部署的思路、代码结构和踩过的坑全部捋一遍适合正在做课设、毕设或者想练手全栈项目的朋友直接参考。1. 项目定位与整体拆解1.1 从标题看这个项目的真实需求“Python基于Vue的酷听音乐网站”这个标题信息量其实很大。拆开来看它至少包含了三个层面的需求第一技术上必须是Python作为服务端语言这意味着你的业务逻辑、数据库操作、接口输出都跑在Python这一侧第二前端必须用Vue来做也就是页面渲染、交互逻辑都在浏览器端以组件化的方式完成第三这是一个“音乐网站”不只是静态页面它得有用户系统、歌曲列表、歌手详情、歌单收藏、搜索、播放器这些核心功能。如果你是要交课程设计或者毕业设计这个标题基本已经给你画好了技术边界。很多同学拿到这种题目第一反应是去搜现成源码但我建议你先别急着搜先搞清楚你未来要面对的验收标准是什么老师大概率会问“这个登录状态是怎么保持的”“歌曲文件放在哪里”“播放器为什么能连续播下一首”。这些问题没有一个合理的答案整个项目就是一盘散沙。我当时给自己的定位是做一个可演示、可扩展的版本用户端能注册登录、看推荐歌单、搜歌、播放管理端Django自带Admin能管理歌手、专辑、歌曲和用户。这个范围听起来不大但真正做起来涉及的东西比你想象的多得多。1.2 整体技术架构与模块划分整个项目的架构其实并不复杂典型的“前后端分离 微服务雏形”前端Vue 3 Vue Router Vuex Axios负责页面渲染、路由跳转、交互事件、播放器组件。后端主Django Django REST Framework负责用户认证、歌曲数据管理、API接口输出。后端辅Flask独立小服务负责从第三方数据源拉取热门歌曲数据爬虫并清洗入库或者生成简单的推荐接口。数据库SQLite先跑通流程后续可以切MySQL。SQLite在Django里是默认配置开发阶段零成本。这样的分层有一个明显的好处每个模块可以单独启动、单独测试。比如我写完Flask爬虫根本不用管前端页面长什么样直接在Postman里调它的接口看返回数据Vue组件写完了后端还没就绪我可以用Mock数据先把页面撑起来。两边并行推进最后再联调。1.3 为什么选择这个组合而不是其他方案有人可能会问都用Python了为什么不用Django一个框架干到底说实话如果你的项目只需要简单展示几首歌那Django足以。但“音乐网站”这个题目有一个隐藏的难点歌曲数据从哪来如果你老老实实用爬虫抓数据Flask写起来会比Django轻快很多因为它没有那么多约定想怎么写就怎么写如果你要让网站看起来更聪明一点比如根据用户收藏推荐相似的歌这个推荐逻辑写成Flask独立服务也是清爽的。Django和Flask的分工我放在下一节详细展开。Vue这边就不用说了前端三大框架里Vue的上手曲线是最友好的。模板语法自然组件化思路清晰对没有系统学过前端的人来说用Vue写一个带播放器的单页应用比原生JavaScript从头撸面包屑导航容易太多。加上Vite工具链的加持冷启动基本秒开调试体验非常舒服。2. 技术选型与开发环境搭建2.1 Django和Flask的分工方案同一个项目里同时出现Django和Flask不是技术洁癖的人大概率会觉得很怪但这套方案在真实企业项目里已经是很成熟的玩法了。核心思路是“各司其职”Django的强项是内置ORM、Admin后台、用户认证体系这些对于一个内容型站点来说全是刚需你自己写要写几百行直接用它的就是现成的Flask的强项是轻、灵活你不想被框架绑住手脚的那部分逻辑统统交给Flask。我在酷听项目里给了它们各自明确的边界Django主站用户表、歌手表、专辑表、歌曲表、歌单表的所有CRUD注册登录、JWT认证、后台管理。对外暴露一套RESTful API格式统一为JSON。Flask服务负责从公开数据接口拉取热门歌曲做数据清洗然后写进数据库。另外做了一个简单的“猜你喜欢”根据用户听歌记录存在Django那边用标签匹配返回推荐歌曲列表。两个服务共享同一个SQLite数据库文件生产环境换成MySQL后共用同一个库即可。Django操作写入的时候Flask那边也能立刻读到因为它们操作的是同一张表。这里有一点要注意如果你用SQLite两个进程同时写可能会碰到锁问题所以Flask爬虫写入时我加了一个简单的重试机制生产环境切MySQL后基本不会碰到这种问题。2.2 开发环境准备Python、Pycharm、Node环境先说Python。建议直接用Python 3.9或3.10Django 4.x和Django REST Framework在3.8以上的兼容性都很好。别用最新的Python 3.12我实测过有些第三方库还没完全适配编译时报错排查起来很浪费时间。Pycharm是开发这个项目最顺手的IDE。注意一点Pycharm分专业版和社区版如果你只是做Django后端开发社区版已经够用完全不需要折腾激活工具网上那些“激活码”很容易给你绑一堆乱七八糟的东西。社区版里新建虚拟环境、运行Django服务、代码提示都有。唯一缺失的是前端开发的支持但Vue代码我们用VS Code打开前端目录来写两个编辑器分工这也是后端工程师的常规操作。Node环境建议装Node.js 16 LTS以上版本npm会一起装好。Vue CLI这个工具链现在有点老了我推荐用Vite来创建Vue 3项目命令是npm create vitelatest music-web -- --template vue跑完之后进入目录安装依赖cd music-web npm install npm install vue-router4 axios vuex4 npm run dev注意vue-router用4.x版本因为它是配套Vue 3的如果用vue-router3会直接报错这个坑网上天天有人问。Vuex也同理4.x才对应Vue 3。2.3 项目初始化与目录结构设计创建一个结构清晰的目录是项目后期不失控的前提。我当时把前后端和辅助服务放在同一个父目录下互相独立各自管理自己的依赖kuting-music/ ├── backend/ # Django 主站 │ ├── manage.py │ ├── config/ # 项目配置settings、urls │ └── apps/ │ ├── users/ # 用户模块 │ ├── music/ # 歌曲模块 │ └── recommend/ # 推荐接口 ├── flask-service/ # Flask 辅助服务 │ ├── app.py │ ├── crawler.py │ └── requirements.txt └── music-web/ # Vue 前端 ├── src/ │ ├── router/ │ ├── store/ │ ├── api/ │ └── views/ └── vite.config.jsDjango项目我习惯新建虚拟环境后手动创建项目和应用这样你能清楚了解每一步。创建Django项目用pip install django djangorestframework django-cors-headers django-admin startproject config . python manage.py startapp users python manage.py startapp music python manage.py startapp recommend创建完应用之后记得在config/settings.py的INSTALLED_APPS里注册这三个应用加上rest_framework和corsheaders。注册这一步漏掉后面所有接口都会报404初学者最常犯的错误就是这个。3. 数据库设计与接口设计3.1 核心数据模型用户、歌手、专辑、歌曲、歌单音乐网站的数据模型其实是比较经典的和电商比要简单一些但关系表的设计依然有讲究。我设计了五张核心表User用户继承Django自带的AbstractUser。Singer歌手字段有姓名、头像、简介、热度。Album专辑属于某位歌手有封面、发行时间、简介。Song歌曲属于某张专辑字段有歌名、时长、音频文件、歌词、播放量。Playlist歌单属于某个用户可以收藏多首歌曲和Song是多对多关系。用Django的ORM来表示大概是这样的代码结构# apps/music/models.py from django.db import models from django.contrib.auth.models import AbstractUser class User(AbstractUser): avatar models.URLField(blankTrue) nickname models.CharField(max_length50, blankTrue) class Singer(models.Model): name models.CharField(max_length100) avatar models.URLField() intro models.TextField(blankTrue) class Album(models.Model): title models.CharField(max_length200) singer models.ForeignKey(Singer, on_deletemodels.CASCADE, related_namealbums) cover models.URLField() publish_date models.DateField() class Song(models.Model): name models.CharField(max_length200) album models.ForeignKey(Album, on_deletemodels.CASCADE, related_namesongs) duration models.IntegerField(default0) # 单位秒 audio_url models.URLField() lyric models.TextField(blankTrue) play_count models.IntegerField(default0) class Playlist(models.Model): name models.CharField(max_length100) owner models.ForeignKey(User, on_deletemodels.CASCADE, related_nameplaylists) songs models.ManyToManyField(Song, throughPlaylistSong, blankTrue) create_time models.DateTimeField(auto_now_addTrue) class PlaylistSong(models.Model): playlist models.ForeignKey(Playlist, on_deletemodels.CASCADE) song models.ForeignKey(Song, on_deletemodels.CASCADE) created_at models.DateTimeField(auto_now_addTrue)这里有几个设计上的关键点ForeignKey里的on_deletemodels.CASCADE表示删除歌手时他名下的专辑和歌曲全部级联删除。这在逻辑上是合理的但实际操作要小心因为歌手表误删一条记录可能连带几百首歌一起没了。所以Admin后台里我给删除操作加了确认提示在代码接口层面更是只用逻辑删除的理念来设计给Singer表加一个is_active布尔字段删除歌手时只把is_active置为False管理员看不到这条数据但数据库中仍然保留。这个做法避免了很多不可逆的误操作。PlaylistSong这个中间表是自己写的而不是直接用ManyToManyField自动生成的表原因是我想在中间表上扩展一个“添加时间”字段。这样前端展示歌单时可以直接按照添加时间排序而且后续如果要加“收藏路由”之类的功能也有地方挂载。3.2 关键API接口设计与认证机制数据模型设计完成之后下一步就是把数据通过API暴露给前端。我用的Django REST Framework来做序列化和视图。先给大家看一下接口清单方法路径作用是否需登录POST/api/auth/register注册用户否POST/api/auth/login登录获取token否GET/api/singers/歌手列表否GET/api/singers/{id}/歌手详情及其歌曲否GET/api/songs/歌曲列表可按关键字搜索否GET/api/songs/{id}/歌曲详情含歌词否POST/api/songs/{id}/play/上报播放次数是GET/api/playlists/歌单列表否POST/api/playlists/创建歌单是POST/api/playlists/{id}/add/向歌单添加歌曲是DELETE/api/playlists/{id}/remove/{song_id}/从歌单移除歌曲是GET/api/recommend/根据用户记录推荐歌曲是认证机制这一块热词里提到了“django cookie 设置 token”说明很多人都在搜索token放在Cookie里的做法。我当时用的是JWT方案前端把token存到localStorage每次请求通过Authorization头带过去。但后来发现有些用户关掉浏览器再打开token还在localStorage里看起来很方便其实存在XSS攻击风险。更稳妥的方案是把token放在HttpOnly的Cookie里这样JavaScript读取不到但Django的CSRF防护会干扰请求。Django里设置HttpOnly Cookie很简单response JsonResponse({ok: True}) response.set_cookie(token, token, httponlyTrue, samesiteLax)读取时在视图函数里从request.COOKIES.get(token)取出。前端Axios需要设置withCredentials: true才能让它自动携带Cookie。这个方案实测下来最省心也没有前端的存储安全问题。3.3 ORM查询与删除对象的实操要点热词里专门有“django执行查询-删除对象”这是所有Django开发者绕不开的操作。我举几个最常用的场景和对应的ORM写法。查询场景比如“按播放量降序取前10首歌”很多人会这么写hot_songs Song.objects.all().order_by(-play_count)[:10]这样没问题但如果你只需要歌曲名称和播放量两个字段用values()或only()限定字段会让查询更快避免把整个对象加载进来hot_songs Song.objects.values(id, name, play_count).order_by(-play_count)[:10]删除场景是坑最多的地方。delete()是立即执行删除的返回一个元组第一个元素是删除的总行数第二个是每个模型删了多少条的字典。如果你想删除某个歌手名下的所有歌曲不需要先查再删ORM会帮你级联删除singer Singer.objects.get(id1) singer.delete()但这里有个不得不提的坑如果你没有设计on_deletemodels.CASCADE删除歌手会报ProtectedError如果设计了CASCADE又容易误删大量数据。所以我强烈建议凡是重要的业务数据除了你说的“物理删除”一定要提供“软删除”方案。我项目里的做法是给每个核心表加is_active字段对外接口默认加一层过滤# 过滤器 class ActiveFilterBackend(filters.BaseFilterBackend): def filter_queryset(self, request, queryset, view): if hasattr(queryset.model, is_active): return queryset.filter(is_activeTrue) return queryset这样前端API永远不会看到软删除的数据而数据和关系都保留在库里。看似多写了一点代码但你在调试的时候会发现这条规则救命的次数特别多。4. 前端Vue实现与播放核心4.1 Vue工程创建与路由设计前端这部分我使用Vue 3 Vite项目结构按模块划分src/ ├── api/ # axios请求封装 ├── assets/ # 静态资源 ├── components/ # 公共组件播放器、导航栏、歌单卡片 ├── router/ # 路由配置 ├── store/ # Vuex状态 ├── views/ # 页面组件 └── App.vue路由设计我参考了音乐类App的常见页面结构/首页推荐位/singer歌手列表/singer/:id歌手详情/playlist/:id歌单详情/search?keywordxxx搜索结果页/login登录页/user个人中心需要登录/player/:id歌曲播放页也可以做成弹出层Vue Router 4创建路由的写法如下// src/router/index.js import { createRouter, createWebHistory } from vue-router const routes [ { path: /, component: () import(../views/Home.vue) }, { path: /singer, component: () import(../views/SingerList.vue) }, { path: /singer/:id, component: () import(../views/SingerDetail.vue) }, { path: /login, component: () import(../views/Login.vue) }, { path: /user, component: () import(../views/UserCenter.vue), meta: { requiresAuth: true } } ] const router createRouter({ history: createWebHistory(), routes }) router.beforeEach((to, from, next) { const token document.cookie.includes(token) || localStorage.getItem(token) if (to.meta.requiresAuth !token) { next(/login) } else { next() } })用懒加载的方式import页面组件首屏加载速度会快得多。路由守卫那块加了一个简单的登录判断如果没有token就跳登录页这个功能虽然简单但演示的时候算是加分项。4.2 核心页面组件拆解页面组件的思路是“组件化复用”。我提炼出来了几个核心组件导航栏组件NavBar.vue固定在顶部包含Logo、搜索框、导航链接、登录/用户头像。搜索框的交互是输入关键字后按回车跳转到/search?keywordxxx。歌单卡片PlaylistCard.vue在首页和歌单列表页复用。展示封面、歌单名、歌曲数量。点击跳转对应的歌单详情页。全局播放器PlayerBar.vue这是整个前端最复杂的组件。固定在页面底部无论路由怎么切换它都在。显示当前播放歌曲名、歌手、播放/暂停按钮、上一首/下一首、进度条、音量调节。播放器的核心是HTML5的audio标签。在Vue里可以这样挂载audio refaudioRef :srccurrentSong.audio_url timeupdateonTimeUpdate endedonEnded/audioonTimeUpdate事件在音频播放过程中高频触发频率大约是250毫秒一次在这个回调里更新进度条的位置。onEnded是当前歌曲播放结束事件处理逻辑是自动切到下一首const onEnded () { if (playlist.value.length 0) return currentIndex.value (currentIndex.value 1) % playlist.value.length play() }这里用模运算实现“播完最后一首自动回到第一首”看起来简单但很多新手在这个位置会写出一堆边界判断。4.3 axios请求封装与跨域处理前端请求后端接口一定要做统一封装而不是在每个组件里裸写axios。我的封装思路是// src/api/request.js import axios from axios const request axios.create({ baseURL: http://localhost:8000/api, timeout: 10000, withCredentials: true }) request.interceptors.response.use( response response.data, error { if (error.response error.response.status 401) { router.push(/login) } return Promise.reject(error) } )然后每个模块的API都从封装后的request继承// src/api/music.js import request from ./request export const getSingerList () request.get(/singers/) export const getSingerDetail id request.get(/singers/${id}/) export const getSongDetail id request.get(/songs/${id}/) export const searchSongs keyword request.get(/songs/, { params: { search: keyword } }) export const getRecommend () request.get(/recommend/)withCredentials: true配合后端Django设置CORS_ALLOW_CREDENTIALS True这组配置缺一不可。如果不加withCredentials前端调用接口时浏览器不会携带Cookie登录状态就失效了。跨域配置在Django的settings.py里CORS_ALLOWED_ORIGINS [ http://localhost:5173, # Vue 开发服务器地址 ] CORS_ALLOW_CREDENTIALS True4.4 关于m3u8播放的补充说明热词里有“vue播放m3u8免安装”这个我们展开说两句。m3u8是HLS视频流的标准格式如果你的音乐网站后续要加MV或者直播功能播放端确实会用到。Vue里播放m3u8不需要额外安装重型播放器SDK使用hls.js这个库配合原生video标签就能搞定。npm install hls.js组件里这样使用import Hls from hls.js const videoRef ref(null) const setupPlayer () { if (Hls.isSupported()) { const hls new Hls() hls.loadSource(https://example.com/live/index.m3u8) hls.attachMedia(videoRef.value) } }但在纯音频场景下m3u8用得很少普通的audio标签直接播放mp3或m4a文件就够了。所以这个功能我留在项目里作为扩展点不默认集成。5. 实操过程与踩坑记录5.1 从零开始的整体开发流程整个项目的开发顺序非常关键顺序对了节奏就快顺序反了会反复返工。我的顺序是这样第一步配置Django环境把数据模型和Admin后台搭起来。用Django Admin是可以直接可视化录入歌手、专辑、歌曲数据的这一步做好了后端的地基就稳了。第二步写接口。从“获取歌曲列表”和“歌曲详情”这两个最简单的接口开始然后用Postman调通。每个接口都要验证参数漏了返回什么数据不存在返回什么用户没登录返回什么。把这些边界情况在这个阶段处理完后期联调会非常顺畅。第三步写前端页面。按照“列表页-详情页-登录注册-播放器”的顺序推进。列表页能显示后端数据后比赛已经赢了一半。详情页能接上参数跳转后再补充播放器。第四步数据填充。光有框架没有内容是没法演示的我写了Flask爬虫从公开接口抓了一部分版权清楚的音乐信息清洗后导入数据库。你也可以手动录入几十首歌效果差不多。第五步前后端联调。重点测试播放器连续播放、登录状态保持、页脚显示是否正常。第六步部署上线。本地全通之后打包前端到后端目录用同一套服务对外提供。这个流程里最容易被新手卡住的是第四步因为“没有数据”会让所有页面看起来都是空的你会分不清是前端渲染有问题还是后端返回有问题。我的建议是在数据导入之前先准备5到10条测试数据手动录进Admin后台让前端有内容可显示然后再用爬虫批量补充。5.2 常见问题排查速查表这两个星期里我栽过的坑拾掇拾掇还真不少整理成一张速查表分享出来基本涵盖了从零搭建最容易遇到的问题。现象可能原因解决方式Vue页面白屏控制台报错路由用history模式但服务端没有配置fallback开发环境下直接用npm run dev部署时后端统一配置兜底渲染index.html跨域请求失败Django没有装django-cors-headers或白名单漏配pip install django-cors-headers在settings里正确加入位置和配置歌曲列表接口返回404应用没有注册到INSTALLED_APPS检查三个业务应用是否都写了进去播放器点击上一首/下一首无反应播放列表数据没传到PlayerBar组件用Vuex管理播放状态所有页面通过store修改播放列表刷新页面路由404Vite开发服务器没有开启history fallback开发环境不用管部署时由后端try_files解决删除歌手后歌曲全没了on_deletemodels.CASCADE的级联删除需要保留数据时改用软删除方案或者设置PROTECT图片显示不出来链接是http://localhost:5173前缀图片URL必须存完整路径且在Django的CORS_ALLOWED_ORIGINS里加对应来源MySQL中文乱码连接参数没有指定utf8mb4配置为OPTIONS: {charset: utf8mb4}Flask写入SQLite报锁多个进程同时写Flask爬虫写入加重试生产上换成MySQL这张表里的每一个问题我都实际遇到过尤其是白屏和404排查了半天才发现是路由模式的问题。所以如果你在部署时碰到404不用怀疑先把后端对前端路由的兜底配置检查一遍。5.3 给课程设计和毕设的几点独家建议除了能跑通我还希望你验收的时候能多讲出一些“设计考量”这也是我和很多过来人交流后认为最加分的地方。第一数据库表一定要加created_at和updated_at字段。看起来没用但它能支撑你讲出“我如何做数据审计”“我如何做最近更新时间排序”这种进阶话题。Django里可以写一个抽象基类class TimeStampedModel(models.Model): created_at models.DateTimeField(auto_now_addTrue) updated_at models.DateTimeField(auto_nowTrue) class Meta: abstract True所有业务表都继承它一张图就能讲清楚。第二播放量不要每次点击都直接play_count 1。高并发下会有性能瓶颈而且很容易被刷。我当时设计了一个简单的Redis方案前端上报播放请求先写到Redis里计数每隔几分钟再由一个Django命令把增量同步到MySQL。如果你不想引入Redis至少也要将播放请求异步化用Django的cache加锁更新。第三搜索功能不要用LIKE %keyword%硬扛。数据量小的时候无所谓但演示时数据量一大中文分词和命中率都会影响体验。我用了Django的SearchVector全文检索效果和速度都提升了一个档次。如果只是排练demo也可以用icontains但提答辩的时候可以主动讲一句“这个搜索后续准备接入Elasticsearch”显得你有意识。第四用户模块不要自己写密码加密。Django的AbstractUser自带set_password()和check_password()拿现成的用就对了。我见过太多人自己写一套SHA256加盐最后认认真真把项目搞出了安全漏洞。6. 部署思路与后续扩展方向6.1 本地完整运行流程当你跟着前面的内容搭完整个项目本地跑起来需要启动三个服务启动Django后端cd backend python manage.py runserver启动Flask辅助服务cd flask-service python app.py启动Vue开发服务器cd music-web npm run dev浏览器打开http://localhost:5173前端页面就会出现。登录、搜索、播放全流程测试确保没有跨域报错、没有白屏之后这个项目在本地就算完整可跑了。6.2 生产环境部署的几个关键配置本地能跑只是第一步如果要放到服务器上给老师或者朋友演示有几个配置是必须调整的第一Django的DEBUG False。这一步会让静态文件无法被Django内置服务器直接服务你需要执行python manage.py collectstatic把前端打包产物和Django自带静态文件收集到一个目录然后用Nginx统一托管。第二Nginx反向代理配置。Nginx要同时托管前端静态文件和反向代理后端接口server { listen 80; server_name your-domain.com; location / { root /path/to/music-web/dist; index index.html; try_files $uri $uri/ /index.html; } location /api/ { proxy_pass http://127.0.0.1:8000/api/; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; } }注意try_files $uri $uri/ /index.html;这一行它的作用是前端路由刷新时兜底返回index.html否则Vue Router的history模式一刷新就404。第三后端如果希望用进程守护我更为推荐的是用supervisorgunicorn来管理Django服务。也就是把python manage.py runserver换掉改成gunicorn config.wsgi:application --bind 127.0.0.1:8000 --workers 3Flask服务如果也跑在同一台机器上可以再用一个supervisor配置管理它的进程。6.3 后续可以扩展的方向做完了这个版本如果你想让它显得更完整或者继续深化技术层次有几个方向我认为最值得扩展。一个是推荐系统。目前“猜你喜欢”只是基于歌曲标签的简单匹配如果你学过协同过滤可以在Flask服务里加一个基于用户的协同过滤算法根据“近期收听记录”计算与其他用户的相似度再返回相似用户喜欢的歌。这个功能很容易讲深在答辩中比较出彩。一个是歌词滚动效果。现在歌词字段已经存在数据库里前端播放器可以按时间标签动态高亮当前句。实现方式就是把歌词按[mm:ss.xx]格式解析匹配audio的currentTime源码量不大但视觉效果很好。还有一个是后台数据可视化。Django Admin本身已经够管理用但如果你想让项目看起来更“现代”可以做一个Vue管理端页面用ECharts展示播放量的趋势、歌手热度Top榜单等。这样前后端分离的味道更明显管理端和前端用户端也形成了两个独立应用。说实话做酷听音乐这个项目的过程本质上就是一次从“会写代码”到“会做项目”的跃迁。写代码只是其中一半的工作剩余一半时间都在跟跨域问题、数据设计、接口约定和部署环境较劲。但正是这些“烦人”的细节才把一个Django新手训练成了能独立交付全栈项目的人。如果你也在折腾类似的音乐网站或者内容型应用希望这篇里的思路和踩坑记录能帮你少走几步冤枉路。最后再分享一个小技巧遇到任何你以为是环境问题的报错先重复读一遍报错信息的前三行八成答案就在里面。
返回列表