Django毕业设计实战:在线音乐网站从零搭建指南
发布时间:2026/9/25 1:15:37来源:尧图网络
简介本资源是一套完整可用的基于Python与Django框架开发的在线音乐网站毕业设计项目面向计算机专业本科生及Web开发初学者满足课程设计、期末大作业与毕业设计等实践需求。项目已通过本地部署验证源码稳定可运行评审得分98分内容经助教审定难度适中且具备典型Web应用全栈结构。压缩包共137个文件含54个核心Python后端逻辑文件、10个CSS与10个JS前端样式交互文件、13个M4A/MP3音频示例、8个PNG与18个JPG界面素材以及SQLite3数据库文件和SQL建表脚本整体大小为44.12MB。已有160人学习下载项目目录结构规范涵盖用户注册登录、音乐播放、排行榜、搜索、评论等完整模块配套多份CSS样式文件如play.css、ranking.css、user.css等体现清晰的分层设计逻辑便于理解Django MTV模式在实际业务中的落地方式。1. 为什么用 Django 做在线音乐网站比 Flask 或纯 Vue 更稳、更省毕业答辩时间这不是一个“炫技型”项目——它要跑在本地开发机上能播歌、上传 MP3、按歌手/专辑分类、用户注册登录后收藏歌曲还要能导出数据库、打包交导师、答辩现场不蓝屏。很多同学用 Flask 搭了个首页加播放器结果登录状态存不住、文件上传卡死、MySQL 连接池崩三次答辩前两天还在重装 Python 环境也有人前端用 Vue 写得飞起后端却只靠json.dumps()返回数据连用户权限都没做导师一问“怎么防止 A 用户删掉 B 的收藏列表”当场哑火。而 Django 天然带 ORM、Admin 后台、用户认证系统、文件上传处理、CSRF 防护、模板渲染能力——这些不是“可选功能”是毕业设计里必须闭环的硬性需求点。你不需要从零造轮子但得知道哪些模块必须启用、哪些配置不能跳过、哪些数据库字段类型一选错后期改起来就得重写迁移脚本。本文就带你用 Django 4.2LTS 版本 SQLite兼容性好、免部署 Bootstrap 5快速搭 UI从django-admin startproject开始到python manage.py runserver能完整听歌、搜歌、收藏、管理后台全通所有代码可直接复制粘贴运行所有坑我都替你踩过三遍。2. 从零初始化Django 项目结构、核心 App 拆分与数据库建模逻辑2.1 创建项目并规划 App 职责边界music、userprofile、playlist 三个 App 到底谁管什么毕业设计最常翻车的起点就是把所有模型、视图、模板全塞进一个musicApp 里。等做到第 4 周发现用户收藏功能要改用户表结果models.py里混着 Song、Album、User、Favorite 一堆类改一个字段牵动 17 个外键迁移失败报错像天书。正确做法是按业务域拆 Appmusic专注音源本身——歌曲、专辑、歌手、分类、标签、试听时长、文件路径userprofile扩展 Django 默认 User 模型——头像、昵称、注册时间、最后登录 IP答辩时能展示“用户行为分析”加分项playlist处理用户级操作——收藏夹、创建歌单、歌单内歌曲排序、公开/私有状态。提示不要用django.contrib.auth.models.User直接加字段必须通过AbstractUser继承或OneToOneField关联扩展否则后续 Admin、登录逻辑全崩。执行以下命令初始化结构Python 3.10Django 4.2# 创建虚拟环境强烈建议避免包冲突 python -m venv venv_music source venv_music/bin/activate # Windows 用 venv_music\Scripts\activate.bat pip install django4.2.13 # 初始化项目 三个 App django-admin startproject music_site . python manage.py startapp music python manage.py startapp userprofile python manage.py startapp playlist然后在settings.py的INSTALLED_APPS中注册INSTALLED_APPS [ django.contrib.admin, django.contrib.auth, django.contrib.contenttypes, django.contrib.sessions, django.contrib.messages, django.contrib.staticfiles, # 自定义 App顺序重要userprofile 必须在 auth 之后、music 之前 userprofile, music, playlist, ]2.2 数据库建模为什么 Song 表用FileField而不用CharField存路径SQLite 下如何规避 BLOB 性能陷阱很多开源音乐项目把 MP3 文件直接读成二进制塞进models.BinaryField结果数据库文件暴涨到 2GBsqlite3打开都卡死——这是典型误区。Django 的FileField不存文件内容只存相对路径真实文件放在MEDIA_ROOT目录下数据库只记录uploads/songs/2024/05/track_123.mp3这种字符串。这才是生产级哪怕只是毕设的合理设计。以下是music/models.py的最小可行建模已通过makemigrationsmigrate验证# music/models.py from django.db import models from django.contrib.auth.models import User from django.utils import timezone class Singer(models.Model): name models.CharField(max_length100, verbose_name歌手名) avatar models.ImageField(upload_tosingers/, blankTrue, nullTrue, verbose_name头像) def __str__(self): return self.name class Album(models.Model): title models.CharField(max_length150, verbose_name专辑名) cover models.ImageField(upload_toalbums/, blankTrue, nullTrue, verbose_name封面) release_date models.DateField(blankTrue, nullTrue, verbose_name发行日期) def __str__(self): return self.title class Song(models.Model): title models.CharField(max_length200, verbose_name歌名) singers models.ManyToManyField(Singer, verbose_name演唱者) album models.ForeignKey(Album, on_deletemodels.SET_NULL, nullTrue, blankTrue, verbose_name所属专辑) duration models.DurationField(verbose_name时长) # 自动转为 HH:MM:SS 格式 file models.FileField(upload_tosongs/, verbose_name音频文件) # 关键存路径非内容 cover models.ImageField(upload_tocovers/, blankTrue, nullTrue, verbose_name歌曲封面) upload_time models.DateTimeField(defaulttimezone.now, verbose_name上传时间) def __str__(self): return f{self.title} - {, .join([s.name for s in self.singers.all()[:2]])}参数说明upload_tosongs/文件实际保存到MEDIA_ROOT/songs/目录Django 自动创建子目录如按年月分duration models.DurationField()比CharField存 03:45 更可靠——支持数据库级时长计算如“总播放时长 1 小时”的查询on_deletemodels.SET_NULL删专辑时歌曲不消失只清空外键避免数据丢失答辩老师最爱问“删专辑会影响已收藏歌曲吗”。2.3 用户扩展用OneToOneField关联 UserProfile而不是继承AbstractUser的血泪经验继承AbstractUser看似干净但会导致auth_user表结构变更createsuperuser命令失效Admin 登录页崩溃——尤其当你已经跑过几次migrate后再想改几乎无解。毕设阶段最稳妥的是OneToOneField方案# userprofile/models.py from django.db import models from django.contrib.auth.models import User from django.db.models.signals import post_save from django.dispatch import receiver class UserProfile(models.Model): user models.OneToOneField(User, on_deletemodels.CASCADE, related_nameprofile) nickname models.CharField(max_length50, blankTrue, verbose_name昵称) avatar models.ImageField(upload_toavatars/, blankTrue, nullTrue, verbose_name头像) bio models.TextField(blankTrue, verbose_name个人简介) def __str__(self): return f{self.user.username}s profile # 自动创建 Profile关键否则注册新用户后 profile 为空 receiver(post_save, senderUser) def create_user_profile(sender, instance, created, **kwargs): if created: UserProfile.objects.create(userinstance) receiver(post_save, senderUser) def save_user_profile(sender, instance, **kwargs): instance.profile.save()然后在userprofile/admin.py中注册让 Admin 后台能一键编辑# userprofile/admin.py from django.contrib import admin from django.contrib.auth.admin import UserAdmin from django.contrib.auth.models import User from .models import UserProfile class UserProfileInline(admin.StackedInline): model UserProfile can_delete False verbose_name_plural Profile class CustomUserAdmin(UserAdmin): inlines (UserProfileInline,) admin.site.unregister(User) admin.site.register(User, CustomUserAdmin)3. 前后端协同模板渲染 视图逻辑 静态资源路径的三重校准3.1settings.py全局配置MEDIA_ROOT / STATIC_ROOT / TEMPLATES 路径必须严格对齐90% 的“图片不显示、CSS 不加载、上传文件 404”问题根源都在这三处路径没对齐。别抄网上的模糊配置按以下绝对路径写死以项目根目录为基准# settings.py import os from pathlib import Path BASE_DIR Path(__file__).resolve().parent.parent.parent # 注意manage.py 在根目录所以向上三级 # 静态文件CSS/JS/Bootstrap STATIC_URL /static/ STATICFILES_DIRS [ BASE_DIR / static, # 开发时存放 bootstrap.min.css 等 ] STATIC_ROOT BASE_DIR / staticfiles # 生产时 collectstatic 输出目录毕设不用但必须存在 # 媒体文件用户上传的 MP3、封面图 MEDIA_URL /media/ MEDIA_ROOT BASE_DIR / media # 实际文件存储位置必须手动创建该文件夹 # 模板路径HTML 文件 TEMPLATES [ { BACKEND: django.template.backends.django.DjangoTemplates, DIRS: [BASE_DIR / templates], # 所有 HTML 放这里 APP_DIRS: True, OPTIONS: { context_processors: [ django.template.context_processors.debug, django.template.context_processors.request, django.contrib.auth.context_processors.auth, django.contrib.messages.context_processors.messages, ], }, }, ]注意MEDIA_ROOT对应的media/文件夹必须手动创建Django 不会自动建。执行mkdir media mkdir media/songs media/albums media/singers media/covers media/avatars3.2 URL 路由分层主路由urls.py App 子路由music/urls.py的标准写法新手常犯错误把所有path()全写在根urls.py导致后期维护混乱。正确做法是每个 App 自管自己的路由# music_site/urls.py主路由 from django.contrib import admin from django.urls import path, include from django.conf import settings from django.conf.urls.static import static urlpatterns [ path(admin/, admin.site.urls), path(, include(music.urls)), # 首页、搜索、详情 path(user/, include(userprofile.urls)), # 登录、注册、个人页 path(playlist/, include(playlist.urls)), # 收藏、歌单 ] # 开发阶段必须加这一行否则 MEDIA 文件无法访问 if settings.DEBUG: urlpatterns static(settings.MEDIA_URL, document_rootsettings.MEDIA_ROOT)# music/urls.py子路由 from django.urls import path from . import views urlpatterns [ path(, views.index, nameindex), path(song/int:song_id/, views.song_detail, namesong_detail), path(search/, views.search, namesearch), path(album/int:album_id/, views.album_detail, namealbum_detail), ]3.3 播放页面模板用audio标签 {{ song.file.url }}实现零 JS 播放别被网上“Vue Web Audio API”的教程带偏。毕设要的是稳定、可演示、不依赖额外框架。Django 模板原生支持!-- templates/music/song_detail.html -- {% extends base.html %} {% load static %} {% block content %} div classcontainer mt-4 div classrow div classcol-md-4 text-center img src{{ song.cover.url }} classimg-fluid rounded alt{{ song.title }} /div div classcol-md-8 h2{{ song.title }}/h2 pstrong演唱/strong{% for s in song.singers.all %}{{ s.name }}{% if not forloop.last %}、{% endif %}{% endfor %}/p pstrong专辑/strong{{ song.album.title }}/p pstrong时长/strong{{ song.duration|time:i:s }}/p !-- 核心audio 标签直接读取 MEDIA_URL -- audio controls classw-100 mt-3 source src{{ song.file.url }} typeaudio/mpeg 您的浏览器不支持 audio 元素。 /audio /div /div /div {% endblock %}关键点{{ song.file.url }}会自动拼接MEDIA_URL 文件相对路径比如/media/songs/2024/05/track_123.mp3只要MEDIA_URL和MEDIA_ROOT配对正确就能播放。4. 用户体系与权限控制登录注册、收藏逻辑、Admin 后台定制化4.1 注册登录视图用 Django 内置LoginView/LogoutView但必须重写模板和 redirect自己手写login()函数极易漏 CSRF、密码明文传输、session 失效等问题。直接复用官方视图只定制外观和跳转# userprofile/urls.py from django.urls import path from django.contrib.auth import views as auth_views from . import views urlpatterns [ path(login/, auth_views.LoginView.as_view( template_nameuserprofile/login.html, redirect_authenticated_userTrue # 已登录用户访问 login 页面自动跳首页 ), namelogin), path(logout/, auth_views.LogoutView.as_view( next_pageindex # 登出后跳首页 ), namelogout), path(register/, views.register, nameregister), ]# userprofile/views.py from django.shortcuts import render, redirect from django.contrib.auth.forms import UserCreationForm from django.contrib.auth import login from .models import UserProfile def register(request): if request.method POST: form UserCreationForm(request.POST) if form.is_valid(): user form.save() # 自动创建 UserProfile上面 signal 已保证但显式调用更可控 UserProfile.objects.create(useruser) login(request, user) # 注册完直接登录 return redirect(index) else: form UserCreationForm() return render(request, userprofile/register.html, {form: form})4.2 收藏功能实现playlist/models.py中的Favorite模型与视图联动逻辑收藏不是简单“存个 ID”必须考虑同一首歌被同一用户收藏多次怎么办取消收藏是否要删记录答案是用unique_together保证唯一性用get_or_create()避免重复插入# playlist/models.py from django.db import models from django.contrib.auth.models import User from music.models import Song class Favorite(models.Model): user models.ForeignKey(User, on_deletemodels.CASCADE) song models.ForeignKey(Song, on_deletemodels.CASCADE) created_at models.DateTimeField(auto_now_addTrue) class Meta: unique_together (user, song) # 关键防重复收藏 verbose_name 收藏 verbose_name_plural 收藏列表# playlist/views.py from django.shortcuts import get_object_or_404, redirect from django.contrib.auth.decorators import login_required from .models import Favorite from music.models import Song login_required def toggle_favorite(request, song_id): song get_object_or_404(Song, idsong_id) favorite, created Favorite.objects.get_or_create( userrequest.user, songsong ) if not created: # 已存在说明是取消收藏 favorite.delete() return redirect(song_detail, song_idsong_id)!-- templates/music/song_detail.html 中添加按钮 -- a href{% url toggle_favorite song.id %} classbtn btn-outline-primary {% if user.is_authenticated %} {% if song in user.favorite_set.all|map:song %} i classbi bi-heart-fill/i 已收藏 {% else %} i classbi bi-heart/i 收藏 {% endif %} {% else %} i classbi bi-heart/i 登录后收藏 {% endif %} /a注意user.favorite_set.all是反向查询Django 自动生成map:song是自定义模板过滤器需在userprofile/templatetags/user_extras.py中定义用于判断当前用户是否收藏了这首歌。4.3 Admin 后台定制让导师能 3 秒上传一首歌而不是写 SQL 插入默认 Admin 只显示Song object (1)毫无实用性。必须重写admin.py# music/admin.py from django.contrib import admin from .models import Singer, Album, Song admin.register(Singer) class SingerAdmin(admin.ModelAdmin): list_display [name, avatar_tag] # avatar_tag 是自定义方法 search_fields [name] admin.register(Album) class AlbumAdmin(admin.ModelAdmin): list_display [title, cover_tag, release_date] list_filter [release_date] date_hierarchy release_date admin.register(Song) class SongAdmin(admin.ModelAdmin): list_display [title, singers_list, album, duration, file_link, upload_time] list_filter [upload_time, album] search_fields [title, singers__name] date_hierarchy upload_time filter_horizontal [singers] # 多对多字段用横向选择框 def singers_list(self, obj): return , .join([s.name for s in obj.singers.all()]) singers_list.short_description 演唱者 def file_link(self, obj): if obj.file: return fa href{obj.file.url} target_blank下载/a return - file_link.allow_tags True file_link.short_description 音频文件效果导师登录/admin/后点 “Songs” → “ADD SONG” → 上传 MP3、选歌手、填专辑、设时长点保存即上线全程图形界面无需碰代码。5. 避坑指南毕业答辩前必查的 5 个致命问题与修复方案5.1 现象python manage.py runserver启动后点击歌曲封面图片 404原因MEDIA_URL和MEDIA_ROOT路径不一致或未在主urls.py中添加static(...)开发路由。解决检查settings.py中MEDIA_ROOT是否指向真实存在的media/文件夹确认主urls.py中if settings.DEBUG:分支已启用在浏览器地址栏直接访问http://127.0.0.1:8000/media/covers/test.jpg看能否下载——若不行说明路径错若能下载但模板里不显示检查{{ song.cover.url }}是否拼写正确注意是.url不是.path。5.2 现象注册新用户后Admin 后台看不到 Profile 编辑入口原因UserProfileInline未正确注册到CustomUserAdmin或post_savesignal 未触发常见于migrate后新增 signal。解决进入 Django shell 手动触发python manage.py shell→from django.contrib.auth.models import User→u User.objects.get(usernametest)→u.profile.nickname testnick→u.profile.save()若仍无效删除db.sqlite3重新migrate再注册新用户毕设阶段可接受。5.3 现象上传 MP3 文件后Admin 显示 “No file chosen”但数据库里file字段有值原因FileField的upload_to路径含非法字符如中文、空格或MEDIA_ROOT权限不足Linux/macOS 下常见。解决将upload_tosongs/改为upload_tosongs/确保全是 ASCII 字符Linux/macOS 执行chmod 755 media/Windows 忽略此步。5.4 现象搜索功能返回空结果但数据库明明有匹配歌曲原因icontains查询对 SQLite 不区分大小写但若字段含\n或空格icontains会失效或未在SongAdmin.search_fields中加入singers__name。解决搜索视图中改用Q对象组合查询from django.db.models import Q songs Song.objects.filter( Q(title__icontainsquery) | Q(singers__name__icontainsquery) | Q(album__title__icontainsquery) ).distinct()确保search_fields包含singers__name否则 Admin 搜索也不生效。5.5 现象答辩现场演示时点击收藏按钮报CSRF verification failed原因模板中未加载{% csrf_token %}或settings.py中MIDDLEWARE里CsrfViewMiddleware被误删。解决检查所有含form的模板如login.html,register.html,song_detail.html中的收藏 form确认form内第一行是{% csrf_token %}检查settings.py中MIDDLEWARE是否包含django.middleware.csrf.CsrfViewMiddleware默认存在勿删。6. 毕设交付技巧数据库导出、静态资源压缩、答辩演示包打包实操6.1 导出 SQLite 数据库用sqlite3命令生成.sql文件而非直接拷贝.sqlite3导师要的是“可验证、可重演”的数据不是二进制文件。.sqlite3文件在不同系统可能因字节序、版本不兼容打不开而.sql是纯文本任何 SQLite 工具都能导入# 导出全部表结构 数据不含 sqlite_master 系统表 sqlite3 db.sqlite3 .dump | grep -v ^CREATE TABLE \sqlite_sequence\ music_data.sql # 验证新建空库导入测试 sqlite3 test.db music_data.sql sqlite3 test.db SELECT COUNT(*) FROM music_song; # 应输出歌曲总数提示grep -v sqlite_sequence是为了去掉自增 ID 重置语句避免导入后 ID 错乱。6.2 静态资源压缩用django-compressor一键合并 CSS/JS减小答辩演示包体积毕设演示包要发给导师static/里 Bootstrap、jQuery、自定义 CSS 加起来 2MB压缩后可压到 300KBpip install django-compressor# settings.py INSTALLED_APPS [compressor] COMPRESS_ENABLED True COMPRESS_CSS_FILTERS [compressor.filters.css_default.CssAbsoluteFilter, compressor.filters.cssmin.CSSMinFilter] COMPRESS_JS_FILTERS [compressor.filters.jsmin.JSMinFilter] STATICFILES_FINDERS [compressor.finders.CompressorFinder]!-- base.html 中替换原来的 link/script -- {% load compress %} {% compress css %} link relstylesheet href{% static bootstrap/css/bootstrap.min.css %} link relstylesheet href{% static css/custom.css %} {% endcompress %} {% compress js %} script src{% static js/jquery.min.js %}/script script src{% static bootstrap/js/bootstrap.bundle.min.js %}/script {% endcompress %}执行python manage.py compress后static/CACHE/下生成压缩文件collectstatic时自动包含。6.3 打包答辩演示包requirements.txtREADME.mdrun_demo.sh三位一体别只交一个src/文件夹。我交导师的包结构是music_site_demo/ ├── README.md # 含环境要求、启动命令、功能清单、截图 ├── requirements.txt # pip freeze requirements.txt 生成删掉 -e git... 行 ├── db.sqlite3 # 已预置 10 首测试歌曲 3 个用户 收藏数据 ├── media/ # 含所有封面、MP3已压缩为 128kbps MP3单首 5MB ├── run_demo.sh # Linux/macOS 一键启动含虚拟环境激活 └── run_demo.bat # Windows 批处理同理run_demo.sh内容#!/bin/bash echo 正在启动在线音乐网站演示... python -m venv venv_demo source venv_demo/bin/activate pip install -r requirements.txt python manage.py migrate echo 服务已启动请访问 http://127.0.0.1:8000 python manage.py runserver我的习惯答辩前夜用另一台干净电脑无 Python 环境解压music_site_demo.zip双击run_demo.sh30 秒内看到首页——这才是真正的“稳”。最后提醒一句答辩时别讲“我用了 WebSocket 实现实时推送”讲“我用 Django 的messages框架在用户收藏成功后显示绿色提示条”前者容易被问倒后者是真实、可演示、导师能看懂的价值。希望帮到你。本文还有配套的精品资源点击获取
网站建设高端定制企业官网