Python+Django在线音乐网站开发实战:从模型设计到部署

发布时间:2026/8/30 3:47:19
Python+Django在线音乐网站开发实战:从模型设计到部署 简介Web开发中选择一个功能完善且生态成熟的框架往往事半功倍。Django作为Python生态的主流全栈框架内置ORM、用户认证、Admin后台等模块为快速构建数据驱动型网站提供了坚实基础。理解MTV架构、数据库模型设计以及文件上传与媒体管理是掌握Django工程实践的关键。无论是SQLite与MySQL的切换还是音频文件的存储与流式播放都体现了后端开发中‘数据与资源分离’的核心思想。这类技术组合广泛适用于内容管理、社交互动、在线教育等场景也是毕业设计中的热门选题。本文围绕在线音乐网站这一典型项目从数据建模、播放器对接、用户权限到部署演示完整梳理基于PythonDjango的实现路径帮助开发者避开常见陷阱高效完成一个功能完整、可演示的Web应用。 做毕设选题目的时候十个里有八个会想到做网站。如果再往上加一些听起来上得了台面的功能比如音乐播放、歌单收藏、评论互动那就几乎锁定了一个方向基于PythonDjango的在线音乐网站。我自己见过太多同学拿着这个题目来问一上来就找“源码”实际自己动手时却卡在模型设计、文件上传、播放器对接这些地方折腾一两周还没跑通。这篇文章就按我的实际开发经验把这个项目从设计思路、数据库建模、核心实现到部署演示完整捋一遍重点说清楚“为什么这样做”而不仅仅是贴代码。不管是拿去做毕业设计还是想自己写一个练手项目这篇都能当你的“外挂”。1. 项目整体设计与技术选型1.1 为什么用Django做在线音乐网站选技术栈是很多人纠结的第一步。只做一个期末小作业用Flask都行但如果目标是“毕业设计”那Django基本是这个题目的最优解。原因有几个自带Admin后台音乐网站天然需要管理端来维护歌手、专辑、歌曲信息。Django Admin只要注册一下模型后台管理页面就出来了不用额外费力气去写一堆管理界面。自带用户认证体系登录、注册、session、密码哈希、权限控制都是内置的音乐网站需要的“登录后才能收藏、评论”这类功能直接基于这套体系扩展就行。自己手写密码哈希和session很容易写出安全问题放在毕设里就是埋雷。ORM省心事音乐、歌手、歌单之间的外键关系、多对多关系在Django ORM里非常直观不用手写一堆SQL JOIN后期改表结构跑一下迁移就行这对毕设来说极其友好。生态成熟、资料多Django的中文资料和社区问答非常丰富遇到大部分报错把错误信息复制到搜索引擎基本都能找到答案这对独立开发经验不多的同学来说太重要了。1.2 MTV架构在音乐网站里怎么落地Django的MTVModel-Template-View架构放到这个项目里就是一套很清晰的职责分工Model负责定义数据结构比如音乐、歌手、用户、评论这些表长什么样。View负责业务逻辑比如用户点了一首歌View就去查库、更新播放量、返回歌曲文件或页面。Template负责页面展示Django的模板语言能直接把歌手姓名、歌曲时长、封面对应的图片URL渲染进HTML。我见过不少同学把所有代码堆在views.py里面一个函数干所有事情最后改一个功能要牵一发动全身。比较好的组织方式是每个业务模块建一个独立的app比如music、user、comment模块之间通过明确的接口交互就像搭积木一样一个萝卜一个坑。这样不仅自己维护省心答辩时老师问你“项目怎么设计的”你也能讲清楚分层逻辑。1.3 项目目录结构与环境版本我建议的项目结构大致是这样的music_website/ ├── manage.py ├── requirements.txt ├── config/ # 项目配置 │ ├── __init__.py │ ├── settings.py │ ├── urls.py │ └── wsgi.py ├── apps/ │ ├── users/ # 用户模块 │ ├── music/ # 音乐模块 │ ├── comments/ # 评论模块 │ └── home/ # 首页模块 ├── static/ # CSS/JS/图片 ├── media/ # 歌曲文件和封面 ├── templates/ # 共用模板 │ ├── base.html │ └── ... └── db.sqlite3 # 本地开发数据库版本选择上我推荐Python 3.10及以上Django使用4.2 LTS版本。这个组合稳定、资料多、新特性全而且Django 4.2是长期支持版本到2026年4月前都有安全更新。这里要注意别用Python 2.x或者Django 1.x那些古董版本和现代写法差异太大遇到问题搜到的解决方案可能全部失效。2. 核心数据模型与数据库设计2.1 音乐网站需要哪些表在线音乐网站看起来功能不多但落到数据库设计上核心实体并不少。我梳理过一份清单基本能覆盖绝大部分毕设需求歌手表Singer姓名、头像、简介、地区、创建时间。专辑表Album专辑名、封面、发行日期、所属歌手。歌曲表Song歌曲名、歌手、专辑、音频文件、歌词、播放量、时长、上传时间。歌单表Playlist标题、封面、所属用户、创建时间。歌单明细表PlaylistItem歌单和歌曲的多对多关系中间表。用户表User直接继承Django自带的AbstractUser即可。收藏表Favorite用户与歌曲的多对多关系记录用户收藏了哪些歌。评论表Comment评论内容、歌曲、用户、父评论、时间。播放记录表PlayRecord哪个用户在什么时间听了哪首歌能拿来展示“最近播放”。实体之间大致是这样一种关系一个歌手有多张专辑一张专辑里有多首歌反过来一首歌一定属于某个歌手和某张专辑。用户和歌曲之间通过收藏表、播放记录表产生联系用户和歌单是一对多歌单和歌曲是多对多。2.2 数据模型的核心代码逻辑下面是一个简化但能直接用的Song模型写法关键点我都加了注释from django.db import models from django.contrib.auth.models import User class Singer(models.Model): name models.CharField(max_length100, verbose_name歌手名) avatar models.ImageField(upload_tosinger/, blankTrue, nullTrue) intro models.TextField(blankTrue, verbose_name简介) created_at models.DateTimeField(auto_now_addTrue) def __str__(self): return self.name class Meta: verbose_name 歌手 verbose_name_plural verbose_name ordering [-created_at] class Album(models.Model): title models.CharField(max_length100, verbose_name专辑名) cover models.ImageField(upload_toalbum/, blankTrue, nullTrue) release_date models.DateField(blankTrue, nullTrue) singer models.ForeignKey(Singer, on_deletemodels.CASCADE, related_namealbums) def __str__(self): return f{self.singer.name} - {self.title} class Song(models.Model): name models.CharField(max_length100, verbose_name歌曲名) singer models.ForeignKey(Singer, on_deletemodels.CASCADE, related_namesongs) album models.ForeignKey(Album, on_deletemodels.SET_NULL, blankTrue, nullTrue, related_namesongs) audio_file models.FileField(upload_tosong/, verbose_name音频文件) cover models.ImageField(upload_tosong_cover/, blankTrue, nullTrue, verbose_name封面) lyric models.TextField(blankTrue, verbose_name歌词) play_count models.PositiveIntegerField(default0, verbose_name播放次数) duration models.CharField(max_length10, blankTrue, verbose_name时长) created_at models.DateTimeField(auto_now_addTrue) def __str__(self): return self.name几个容易踩坑的点on_delete参数一定要显式指定新版Django里外键必须传这个参数。歌手删除后歌曲怎么办最常见的是CASCADE级联删除比较安全的是SET_NULL搭配nullTrue这样歌手被删了歌曲还在只是关联信息变成空。upload_to建议按照业务类型分目录比如音频放song/封面放album/不然media目录下会堆成一片混沌。related_name最好明确指定不然后续写ORM时会出现一堆奇怪的song_set绕来绕去把自己绕晕。2.3 SQLite和MySQL之间的选择毕设项目本地演示、上传到GitHub给老师看SQLite足够了零配置、单文件、搬运方便。但如果数据量大、并发访问高或者部署到云服务器上SQLite容易遇到“database is locked”问题这种情况就应该换成MySQL。切换到MySQL时除了在settings里改数据库配置还要注意创建数据库时指定UTF-8字符集否则中文很容易变成问号CREATE DATABASE music_db DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;安装连接驱动pip install pymysql # 然后在项目__init__.py里添加 import pymysql pymysql.install_as_MySQLdb()3. 音乐播放与文件管理的核心实现3.1 音频文件到底存在哪里这是一个容易被忽视但对毕设项目很核心的问题歌曲文件是直接存数据库还是存文件系统、数据库只存路径答案很明确存文件系统数据库里只存文件的路径URL。把音频文件以BLOB字段塞进数据库会让数据库文件膨胀得飞快备份、迁移都变得极其痛苦播放时还要把整个BLOB读出来再传给浏览器性能和资源消耗都很差。Django的FileField本质上就是把文件放到MEDIA_ROOT目录下然后在数据库里存一个相对路径字符串。在settings里这么配MEDIA_URL /media/ MEDIA_ROOT BASE_DIR / media然后根URL文件里加上静态服务from django.conf import settings from django.conf.urls.static import static urlpatterns [...] if settings.DEBUG: urlpatterns static(settings.MEDIA_URL, document_rootsettings.MEDIA_ROOT)这样模板中就能直接用{{ song.audio_file.url }}拿到可访问的音频地址。3.2 网页播放器对接在Django模板里播放音乐其实就是HTML5的audio标签加上歌曲文件的URL没有任何魔法audio controls source src{{ song.audio_file.url }} typeaudio/mpeg 您的浏览器不支持音频播放。 /audio不过如果只做到这一步体现不出一个在线音乐网站的气质。我建议加一个全局播放栏就是那种页面底部固定、切换页面也不中断播放的播放器。做法很简单把当前播放列表和当前播放索引放在JavaScript全局变量里点击歌曲时动态更新audio的src并调用play()。具体可以这样let playList []; // 当前播放列表 let currentIndex 0; function playSong(songId, songName, songUrl, coverUrl) { const audio document.getElementById(globalAudio); audio.src songUrl; audio.play(); // 更新播放器UI document.getElementById(nowPlayingName).innerText songName; // 记录到后端 recordPlay(songId); } function nextSong() { currentIndex (currentIndex 1) % playList.length; playSong( playList[currentIndex].id, playList[currentIndex].name, playList[currentIndex].url, playList[currentIndex].cover ); }这里有一个很现实的问题服务端怎么知道播放了一首歌你不希望每次切歌都刷新页面所以要写一个异步上报接口比如POST /api/song/play/前端每次播放新歌时往后台发送一个歌曲ID和时间戳后端据此更新播放次数和播放记录。3.3 上传音乐的校验和封面上传如果你打算做一个后台管理页面让管理员上传歌曲那么文件校验这块儿必须处理好。我踩过最深的坑就是前端只校验扩展名恰好某个用户上传了一个改名为.mp3的垃圾文件后台播放时直接卡死。建议在后端同样校验至少做两层def validate_audio_file(file): # 扩展名校验 ext os.path.splitext(file.name)[1].lower() if ext not in [.mp3, .wav, .ogg, .m4a, .flac]: raise ValidationError(不支持的音频格式) # 大小校验限制为20MB if file.size 20 * 1024 * 1024: raise ValidationError(文件大小不能超过20MB) return True封面上传同理建议用Pillow库把图片尺寸压缩一下避免用户传了一个5MB的大图页面加载速度被拖死。代码大致这样from PIL import Image def compress_cover(image_field, max_size(400, 400)): img Image.open(image_field) img.thumbnail(max_size) img.save(image_field.path, quality85, optimizeTrue)3.4 关于“流媒体播放”的小加分项如果想让毕设更亮眼可以提一下“流式播放”。原理其实不复杂Django视图里用FileResponse配合HTTP Range头去读取文件的字节片段浏览器就能实现拖动进度条、续传播放。默认的FileResponse在Django 4.2里已经支持了这个行为直接用即可from django.http import FileResponse def stream_song(request, song_id): song get_object_or_404(Song, pksong_id) # 这里可以顺便更新播放次数 song.play_count 1 song.save(update_fields[play_count]) response FileResponse(song.audio_file.open(rb), content_typeaudio/mpeg) return response答辩时如果老师问“你的网站支持边下边播吗”你就可以理直气壮地说支持因为FileResponse本身就是按块读取文件的。4. 用户系统、权限与业务功能串联4.1 自定义用户模型还是直接用内置UserDjango自带的User模型提供了用户名、密码、邮箱、first_name、last_name等字段对于音乐网站来说勉强够用但想给用户加一个“头像”字段、一个“个性签名”字段就麻烦了——扩展内置User有两种方案在Django 1.8之后官方推荐的做法是自己定义User模型并继承AbstractUser这样想加什么字段加什么字段。但注意必须在第一次迁移数据库之前就设置好否则中途改User模型会让你想哭。如果项目已经跑起来了不想折腾User模型那可以新建一个Profile模型一对一关联内置User扩展字段都放在Profile里面。对于新开工的毕设我建议直接用第一种继承AbstractUser。代码非常简单from django.contrib.auth.models import AbstractUser class User(AbstractUser): avatar models.ImageField(upload_touser_avatar/, blankTrue, nullTrue) nickname models.CharField(max_length50, blankTrue) bio models.TextField(blankTrue, verbose_name个人简介)然后在settings里指定AUTH_USER_MODEL users.User注册和登录建议直接用Django自带的UserCreationForm改造不要自己手工拼接密码哈希。Django的make_password、check_password这套内置机制已经处理了盐值和加密算法升级自己造轮子很容易出现“密码明文存储”这种致命问题。4.2 收藏、评论、歌单功能逻辑怎么串用户登录之后核心交互无非三块收藏用户点击收藏按钮服务端判断是否已收藏没收藏则写入Favorite表已收藏则删除记录实现收藏/取消收藏的切换。评论登录用户可以对歌曲发评论也可以对别人的评论进行回复。为了简单可以只做一级评论加一个parent字段前端展示时如果有parent就对楼主做“回复”关联展示。歌单用户可以创建歌单、删除歌单、往歌单里添加歌曲。歌单和歌曲之间用中间表维护常用的Django写法是class Playlist(models.Model): title models.CharField(max_length100) user models.ForeignKey(User, on_deletemodels.CASCADE, related_nameplaylists) songs models.ManyToManyField(Song, throughPlaylistItem, related_nameplaylists) cover models.ImageField(upload_toplaylist_cover/, blankTrue, nullTrue) class PlaylistItem(models.Model): playlist models.ForeignKey(Playlist, on_deletemodels.CASCADE) song models.ForeignKey(Song, on_deletemodels.CASCADE) added_at models.DateTimeField(auto_now_addTrue) class Meta: ordering [added_at]这样相当于给歌单里的歌曲保留了一个“添加时间”字段后续展示歌单时能按加入顺序排列体验更好。4.3 权限控制哪些页面必须登录才能看在线音乐网站的常见权限设计是浏览歌曲、搜索歌曲、播放歌曲不需要登录但如果要收藏、评论、创建歌单就必须登录。Django里最简单的实现是使用login_required装饰器。如果是基于类的视图使用LoginRequiredMixin。举个例子from django.contrib.auth.decorators import login_required from django.http import JsonResponse login_required def add_favorite(request, song_id): song get_object_or_404(Song, pksong_id) obj, created Favorite.objects.get_or_create(userrequest.user, songsong) if not created: obj.delete() return JsonResponse({status: removed}) return JsonResponse({status: added})这里用到的get_or_create是一个非常实用的查询技巧先查一条记录是否存在不存在就创建存在就返回。同时它返回一个元组(obj, created)第二个元素表示到底是不是新创建的——省了一次先查后写的逻辑。前端配合一个Ajax请求就能做到点击收藏按钮时不刷新页面状态实时切换。4.4 搜索功能的实现思路音乐网站的搜索一般按歌曲名、歌手名、专辑名来搜。最朴素的实现方式是用ORM里的icontainsfrom django.db.models import Q def search(request): keyword request.GET.get(q, ).strip() songs Song.objects.filter( Q(name__icontainskeyword) | Q(singer__name__icontainskeyword) | Q(album__title__icontainskeyword) ).distinct() if keyword else [] return render(request, search_result.html, {songs: songs, keyword: keyword})icontains在SQLite里会翻译成LIKE查询不区分大小写做毕设完全够用。如果后面想加更高级的全文搜索可以引入django-haystack但对毕设而言用LIKE已经能讲明白自己的搜索逻辑了。搜索接口最好做成支持GET参数形式这样后续做分页、排序都方便扩展。5. 前端渲染与页面交互的实现5.1 模板继承和静态文件管理Django的模板继承机制是开发多页面网站的效率神器。你只需要写一个base.html把页头、导航栏、页脚、全局播放器都放在里面然后在子模板里只用{% extends base.html %}和{% block content %}去填充内容区域。这样如果后期想给导航栏加一个菜单项只需要改一个文件所有页面同步更新。静态文件管理上Django的做法是把CSS和JS文件放在每个app的static目录下或者统一放在项目根目录的static下collectstatic命令负责把它们收集到一个统一目录供部署使用。在settings里配置STATIC_URL /static/ STATICFILES_DIRS [BASE_DIR / static]5.2 用原生JS还是引入Vue很多同学在“要不要用Vue”上犹豫。我的建议是毕设项目用原生JavaScript就够了最多引入一个jQuery。原因很简单Django的模板渲染天然是服务端渲染用Vue反而多一层复杂度还得处理跨域、API设计等问题。原生JS配合Ajax请求Django的JSON接口已经能覆盖音乐网站绝大部分交互需求。比如页面加载时渲染歌曲列表用Django模板直接循环输出div classsong-list {% for song in songs %} div classsong-item>python manage.py makemigrations python manage.py migrate第一行会根据模型变化生成迁移文件第二行把迁移应用到数据库。这对毕设来说好处太明显了——你不需要手写ALTER TABLE语句不用担心破坏已有数据Django会记录哪些迁移已经执行过。但这里有个很常见的坑改models.py的时候如果字段加了一个非空限制比如nullFalse而数据库里已经有老数据了迁移时会提示你“给已有数据提供默认值”。这时直接在字段定义里加default或者blankTrue就能轻松过去没必要真的去提供全局默认值。6.2 数据备份与导出SQL毕设提交的时候老师可能要求你提供数据库文件或者SQL脚本。SQLite的话很简单把.sqlite3文件直接拷出来就行。MySQL的话可以用命令行工具导出mysqldump -u root -p music_db music_db.sql如果不想装MySQL客户端也可以在Django代码里写一个自定义的管理命令把当前数据库的数据导出为JSON# apps/music/management/commands/export_data.py from django.core.management.base import BaseCommand from django.core import serializers import os class Command(BaseCommand): help 导出数据库到JSON文件 def handle(self, *args, **options): fixtures_dir os.path.join(fixtures) os.makedirs(fixtures_dir, exist_okTrue) for model_name in [auth.User, music.Singer, music.Album, music.Song]: output os.path.join(fixtures_dir, f{model_name.split(.)[1]}.json) with open(output, w, encodingutf-8) as f: data serializers.serialize(json, serializers.deserialize(...)) # 简写生产JSON数据 self.stdout.write(fExported {model_name})这个导出文件在换电脑、重新部署时可以用loaddata导回去方便得不行。6.3 从本地演示到服务器部署毕业设计一般要求现场演示演示有两种方式一种是在自己笔记本上跑起来访问127.0.0.1:8000另一种是部署到一台云服务器上用IP或者域名访问。如果选择本地演示有几个细节要注意ALLOWED_HOSTS要配置成[*]或者包含你的局域网IP不然手机访问电脑IP时会报DisallowedHost错误。演示前确保python manage.py makemigrations和migrate都执行过否则数据库缺表直接白屏。把DEBUG设为True演示最方便因为能看到完整的错误页面。如果部署到服务器那我推荐用nginx gunicorn的组合大致流程# 安装依赖 pip install gunicorn # 收集静态文件 python manage.py collectstatic # 用gunicorn启动 gunicorn config.wsgi:application -b 0.0.0.0:8000nginx负责处理静态文件和反向代理把请求转发给gunicorn。因为Django的runserver只适合开发调试性能扛不住生产环境。6.4 演示时的几个小技巧答辩那天最怕的就是现场翻车我在这一点上非常有发言权。给大家几个实战经验提前准备好一份带真实数据的数据库里面要有一些热度较高的歌曲、几位知名歌手、几个创建好的歌单。到时候演示时点开页面就内容丰富观感极佳。不要现场注册用户再慢慢添加歌曲那样演示节奏会拖得很长。提前在电脑上跑通一遍完整的“用户注册—登录—搜索—播放—收藏—评论”流程把每一步的预期结果记下来。演示的时候照着这个流程走绝对比临场乱点要稳。浏览器建议用Chrome并且开一个隐身窗口演示登录、注册流程避免缓存cookie导致登录状态错乱。准备一条“演示脚本”就是一份sop文档包含了每一步要点击什么、展示什么功能、讲什么亮点。这样万一脑子一片空白照着念也能撑完整个演示。7. 常见问题与排查技巧实录7.1 数据库迁移报错汇总做Django项目几乎没有人能一帆风顺完成所有迁移。我整理了几个高频报错报错信息原因解决方案No changes detected模型没变化或app没有被注册进INSTALLED_APPS检查settings里的INSTALLED_APPS是否包含该appTable xxx already exists迁移和数据库状态不一致用python manage.py migrate app_name --fake标记为已执行Field id expected a number but got xxx主键字段手动赋值错误不要手动给自增主键赋值OperationalError: no such column: xxx数据库和模型不同步执行makemigrationsmigratedjango.db.utils.IntegrityError: NOT NULL constraint failed新增非空字段但没有默认值给字段加default或blankTrue其中一个我感到过多次的痛苦经历是“删除了一张表重新改模型后migrate不生效”。这种情况最干净的办法是把migrations目录下的文件除了__init__.py全删了然后把db.sqlite3文件删除重新makemigrations和migrate。开发阶段数据不重要的情况下这么做可以省下大量排查时间。7.2 静态文件和媒体文件404这是所有Django新手都会遇到的坑设置好STATIC_URL和MEDIA_URL后图片和CSS还是加载不出来。排查思路依次是开发模式下确认是否在根urls里加了static()配置。确认文件确实在MEDIA_ROOT对应的目录下。用浏览器开发者工具看Console里的404路径判断是少了前缀还是路径拼接错误。如果部署到服务器记得执行collectstatic并且nginx要正确指向STATIC_ROOT目录。我见过一个特别容易踩的路径拼接问题在模板里直接用/media/{{ song.audio_file }}而忘了audio_file本身已经包含了路径。正确做法是用{{ song.audio_file.url }}Django会基于MEDIA_URL自动拼接完整URL。7.3 中文乱码问题SQLite通常不会出现中文乱码MySQL如果创建表时没有指定UTF-8字符集就会出现中文变成???或者å¯å®¶这种乱码。我建议在连接MySQL时在settings里显式指定OPTIONS: { charset: utf8mb4, },另外Django模板页面的HTML里也要声明编码meta charsetUTF-87.4 播放没声音/加载缓慢如果本地演示时歌曲卡顿、加载慢先看是不是音频文件体积太大。一首未压缩的WAV文件可能达到几十MB网页加载自然慢。建议在测试数据中选择几首MP3格式、大小在2-5MB的歌曲文件。还有一个经常被忽略的问题浏览器直连127.0.0.1:8000时速度还行但如果用局域网IP访问手机通过WiFi取文件会受路由器带宽影响大文件就会缓冲。所以演示前先把歌曲文件压缩或者只准备4-5首歌作为演示数据就够了。8. 源码组织和自动化给项目加分8.1 写一份让别人能看懂的项目README毕业设计提交源码时源码包里的README.md质量直接决定评审老师的第一印象。我见过太多源码塔了解压以后不知道怎么跑起来。一份好的README至少应该包含项目简介这个项目是什么实现了哪些功能。技术栈Python版本、Django版本、用到的第三方库。快速启动步骤克隆代码 → 创建虚拟环境 → 安装依赖 → 执行迁移 → 创建超级管理员 → 运行开发服务器 → 访问地址。默认账号如果提供了测试账号写清楚用户名和密码。项目结构用树状图列出主要文件和目录。功能列表分模块列出已实现的功能点。启动步骤我一般写成这样# 1. 进入项目目录 cd music_website # 2. 创建虚拟环境 python -m venv venv # 3. 激活虚拟环境 # Windows: venv\Scripts\activate # macOS/Linux: source venv/bin/activate # 4. 安装依赖 pip install -r requirements.txt # 5. 数据库迁移 python manage.py makemigrations python manage.py migrate # 6. 创建管理员账号 python manage.py createsuperuser # 7. 启动服务器 python manage.py runserver这几步走完任何同学都能在不借助外部帮助的情况下把项目跑起来。8.2 requirements.txt怎么生成记住一条命令pip freeze requirements.txt但freeze会把你环境里所有包都列出来有些是无关的。更专业的做法是用pipreqs工具生成它只扫描项目实际用到的包pip install pipreqs pipreqs ./ --force这样生成的requirements.txt更干净避免在别人电脑上安装一堆没用的依赖。这个细节很微小但能看出项目是否规范。8.3 用Git管理源码给自己留条后路哪怕只是毕设我也强烈建议用Git做版本管理。在项目最早期就执行git init每次改好一个功能就提交一次这样每次改动都有记录改坏了可以回滚。源码包里带着.git目录还会让材料显得更专业。如果你愿意还可以把项目推到GitHub上开一个私有仓库随时多一份异地备份比只放在U盘里安全得多。不过要注意如果使用别人提供的素材、模板或开源代码一定要看清开源协议避免出现版权问题影响毕业审查。最后分享一点我实际开发中的感受把这个音乐网站从头到尾完整做一遍你对Django的理解会突飞猛进。我一开始也是照着别人的项目抄跑到一半发现数据模型根本不适合自己的功能需求又推倒重来。回过头看最值得投入时间的其实不是堆功能而是把基础模型设计好想清楚每张表之间的关系后面的功能开发会顺畅得多。我还有一个亲身教训网上很多免费“源码”里面塞了冗余的代码甚至藏了后门直接下载下来跑很危险。如果只用来学习参考把设计思路读懂了就够了最后还是要自己动手写一遍把技术点变成自己的储备。等你真正写完这个项目再被问到“你项目里遇到过什么坑”时你绝对能自信地讲出好几个让老师点头的经历。本文还有配套的精品资源点击获取