Django第一次作业实战:环境配置、增删改查与后台管理全流程
2026/9/17 4:22:00 网站建设 项目流程

说实话,看到“django第一次作业”这个标题,我就想起自己当年那会儿的状态:官方教程刷了一遍,觉得Django就是“装个环境、起个服务、看到一个火箭页面”,可真到要交作业的时候,才发现连python manage.py runserver之后该干嘛都发懵。第一次做Django作业,大部分人的卡点根本不在“逻辑难”,而在于“不知道作业到底要交什么”以及“环境没跑起来就先想放弃”。

这篇文章就是给正准备交第一次Django作业的朋友准备的。我会用一套完整的实操过程,把从环境搭建、创建项目和应用、数据库选型与坑点、增删改查、文件下载到后台管理界面这一系列第一次作业里最常涉及的内容全部过一遍。无论你的作业是“做个简单博客”还是“写个多媒体资源管理系统”,这套流程都能直接拿来参考,看完不仅能把作业跑起来,还能知道每一步背后的原因,遇到问题也不会两眼一抹黑。

1. 拿到作业先别急着写代码:先做任务拆解和环境准备

我第一次做Django作业时吃了大亏,就是拿到题目直接开写,结果写到一半发现项目结构是乱的,又要推到重来。所以我现在的习惯是,不管作业多着急,先花半小时把“作业到底要我交什么”拆明白。

1.1 判断你的作业属于哪种类型

Django第一次作业,翻来覆去逃不出下面这几种形态:

  • 环境验证型:只要能跑起runserver,浏览器里看到默认的火箭页,访问/admin能看到后台登录界面,基本就及格了。这种作业主要是逼你装好环境,理解“框架能跑起来”这件事。
  • 带模型和后台的增删改查型:需要你定义数据模型(比如文章、图书、资源条目),迁移到数据库,在admin后台里能对数据进行添加、删除、修改、查询,可能还需要写几个前端页面展示数据。这是目前高校作业里最常见的一种。
  • 综合实战型:题目会要求做一个“XX管理系统”,前端有浏览列表、详情页,后台能管理数据,甚至带文件上传和下载功能,数据存放在MySQL里。热搜词里看到的“django多媒体资源管理系统实战包:含mysql数据库、后台管理、前端浏览与下载功能”大致就是这个level。

判断好作业属于哪种类型之后,再往下走就会轻松很多。如果连题目要求都模棱两可,建议直接问老师或者助教,比闭门造车高效一百倍。很多第一次作业翻车,都是因为理解偏了需求,做的事情和验收标准对不上。

1.2 环境准备:别在第一步就放弃

不管哪种作业,第一步都是把环境跑通。我的建议是:不要直接在系统Python环境里装Django。不是说不能用,而是多个项目混在一起时,版本冲突能让你哭不出来。第一次作业就养成用虚拟环境的习惯,往后省心太多。

在项目目录下执行这几条命令:

mkdir django_homework cd django_homework python -m venv venv

Windows下激活虚拟环境:

venv\Scripts\activate

macOS/Linux下激活:

source venv/bin/activate

激活成功后,命令行前会出现(venv)字样。接着安装Django:

pip install django

这里有个小常识:pip install django默认装的是当前最新稳定版,比如Django 4.x或5.x。第一次作业用最新版完全没问题,老师验收时基本不会因为Django版本卡你。如果你要装指定版本,用pip install django==4.2.11这种写法。

装完以后验证一下:

python -m django --version

能看到版本号输出,说明Django主体环境已经OK了。

1.3 创建项目和应用的顺序,很多人搞反了

好多第一次写Django作业的人分不清“项目(project)”和“应用(app)”的关系。简单说:

  • 项目是你的整个网站,是一个容器。
  • 应用是项目里的一个个功能模块,比如文章模块、用户模块、资源模块。

一个项目可以包含多个应用,一个应用也可以被多个项目复用。这个角度理解透了,就不会把代码全部塞进一个文件里了。

创建项目:

django-admin startproject mysite cd mysite

创建应用(注意:必须先进入项目根目录,也就是包含manage.py的那个目录):

python manage.py startapp myapp

创建完以后的目录结构大致是这样:

mysite/ ├── manage.py ├── mysite/ │ ├── __init__.py │ ├── settings.py │ ├── urls.py │ └── wsgi.py └── myapp/ ├── __init__.py ├── admin.py ├── apps.py ├── models.py ├── tests.py └── views.py

很多人创建完应用之后,直接开始写代码,结果admin后台里看不到自己新建的模块。原因很简单:你还没有把应用注册到项目里

打开mysite/settings.py,找到INSTALLED_APPS列表,把myapp加进去:

INSTALLED_APPS = [ 'django.contrib.admin', 'django.contrib.auth', 'django.contrib.contenttypes', 'django.contrib.sessions', 'django.contrib.messages', 'django.contrib.staticfiles', 'myapp', # 这里,注册你自己的应用 ]

顺手把这两行也改了,否则后台显示的语言和时区会让你别扭:

LANGUAGE_CODE = 'zh-hans' TIME_ZONE = 'Asia/Shanghai'

改好之后,跑一次:

python manage.py runserver

浏览器打开http://127.0.0.1:8000,看到Django默认的页面,说明项目已经跑起来了。第一次作业做到这一步,相当于完成了环境冒烟测试。

2. 数据模型与数据库:mysqlclient这个坑,我希望你一次就跳过去

如果作业只是“起个服务”,那第一部分就够了。但绝大多数第一次作业都要“存数据”,这就绕不开数据库和模型设计。

2.1 到底用SQLite还是MySQL

Django默认用的是SQLite,零配置就能跑。但我第一次做作业时,老师要求必须用MySQL,说“不能用SQLite交差”。这个要求其实很常见,因为企业里生产环境基本不用SQLite。

我的建议是:第一次作业,能选SQLite就先用SQLite把功能做出来,最后再切换到MySQL。先跑通逻辑,再换数据库,这样卡点能分离开。如果作业明确要求MySQL,那就直接上MySQL,别心存侥幸。

无论用哪种,Django的ORM都能让你用同一套代码操作数据库。你写的models.py基本不用改,只需要改settings.py里的DATABASES配置,然后重新迁移就行。

2.2 mysqlclient安装:Windows、Linux下的三个常见解法

如果作业要求MySQL,第一步就是装mysqlclient。这个库是Django连接MySQL的关键驱动,也是我第一次作业卡最久的地方。pip install mysqlclient在Windows上直接执行,想一次成功很难,因为它在安装时要编译C扩展。

常见解法有三种:

**第一种,Windows用户直接装预编译的whl文件。**去网站(比如https://pypi.org/project/mysqlclient/#fileshttps://github.com/PyMySQL/mysqlclient-python)找对应Python版本的whl文件,然后:

pip install mysqlclient‑2.2.0‑cp311‑cp311‑win_amd64.whl

注意文件名里的cp311表示Python 3.11,win_amd64表示64位Windows,要对上号。

**第二种,Linux/macOS用户先装系统依赖再装。**Ubuntu/Debian系统:

sudo apt-get install python3-dev default-libmysqlclient-dev build-essential pip install mysqlclient

macOS系统:

brew install mysql-client export PATH="/usr/local/opt/mysql-client/bin:$PATH" pip install mysqlclient

第三种,如果实在装不上mysqlclient,可以用PyMySQL代替。

pip install pymysql

然后在mysite/__init__.py里加上:

import pymysql pymysql.install_as_MySQLdb()

这样做很省事,但要注意:PyMySQL是用纯Python写的,性能比mysqlclient差一些,第一次作业完全足够,可给老师的“技术印象”不如mysqlclient好。

提示:如果作业是在线评测或者老师要求“你的项目在别人机器上能直接跑”,最好在项目里放一个requirements.txt,把依赖版本锁定出来。执行pip freeze > requirements.txt就能生成。

2.3 配置DATABASES:注意utf8mb4这个细节

MySQL能跑之后,打开settings.py,把DATABASES配置改成:

DATABASES = { 'default': { 'ENGINE': 'django.db.backends.mysql', 'NAME': 'django_homework', 'USER': 'root', 'PASSWORD': '你自己的密码', 'HOST': '127.0.0.1', 'PORT': '3306', 'OPTIONS': { 'charset': 'utf8mb4', }, } }

这里有个细节很多人忽略:OPTIONS里的charset一定要用utf8mb4,不是utf8utf8mb4是MySQL的“真UTF-8”,能存emoji和特殊生僻字。第一次作业不涉及这些还好,但如果作业要求做一个资源管理系统并支持中文搜索,用错字符集在后续查询中文时会出现乱码问题。

另外,记得提前在MySQL里创建好数据库:

CREATE DATABASE django_homework DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;

如果不先建库直接迁移,会报Unknown database错误,这个坑很常见。

2.4 写第一个模型:从“资源表”理解ORM

现在假设你的作业是“多媒体资源管理系统”,我们需要一张表来存资源信息。打开myapp/models.py,写:

from django.db import models class Resource(models.Model): title = models.CharField(max_length=200, verbose_name='标题') file = models.FileField(upload_to='resources/', verbose_name='文件') description = models.TextField(blank=True, verbose_name='描述') created_at = models.DateTimeField(auto_now_add=True, verbose_name='创建时间') def __str__(self): return self.title class Meta: verbose_name = '资源' verbose_name_plural = verbose_name

这套代码里,CharField对应数据库的varchar,FileField对应文件上传路径,auto_now_add=True表示创建时自动写入当前时间。

理解ORM的一个关键是:你写的类是表结构,不是数据。每个类属性对应一个数据库字段,每个实例对应一行数据。比如Resource.objects.all()就是在查这张表的全部数据。

写完模型后,执行:

python manage.py makemigrations python manage.py migrate

makemigrations是根据模型的变化生成迁移文件,作用有点像“把需求写成SQL草稿”;migrate才是真正把改动同步到数据库。第一次作业里,如果你改了models.py发现数据库没变化,大概率就是只跑了makemigrations忘了跑migrate

2.5 在admin后台里看到你的模型

默认情况下,自定义的模型不会出现在admin后台里。需要打开myapp/admin.py

from django.contrib import admin from .models import Resource admin.site.register(Resource)

然后创建一个管理员账号:

python manage.py createsuperuser

按提示输入用户名、邮箱、密码。登录http://127.0.0.1:8000/admin,就能看到“资源”这张表了,可以在后台添加、修改、删除数据。做完这一步,你的Django作业已经完成了“后台管理”这个核心验收点。

3. 核心业务逻辑:查询、删除与页面渲染

作业里最常要求的不是单纯的后台管理,而是“前台页面能浏览列表、能查看详情”,甚至“能执行删除操作”。这段我会把视图函数、URL配置、模板渲染和ORM查询/删除完整串一遍。

3.1 URL配置:Django怎么知道找哪个视图

Django的工作模式可以简化成:用户请求一个URL,Django根据URL匹配规则,找到对应的视图函数,视图函数处理完业务后返回一个响应

项目根路由文件是mysite/urls.py。为了模块化,建议在myapp里也建一个urls.py

from django.urls import path from . import views urlpatterns = [ path('', views.index, name='index'), path('resource/<int:pk>/', views.resource_detail, name='resource_detail'), path('resource/<int:pk>/delete/', views.resource_delete, name='resource_delete'), ]

然后在项目的mysite/urls.py里用include把这个子路由挂上去:

from django.urls import path, include urlpatterns = [ path('admin/', admin.site.urls), path('', include('myapp.urls')), ]

这里值得多提一句:name='index'这种命名不是可有可无的。模板里用{% url 'index' %}这种写法时,是根据name反向解析出URL地址的。如果当初在模板里写死了/或者/resource/1/,后续一改URL就全盘崩掉;用name的话,改路由只改一处就行。第一次作业养成这个习惯,显得你很有工程意识。

3.2 列表页和详情页:ORM查询的入门操作

打开myapp/views.py,写一个最简单的列表视图:

from django.shortcuts import render, get_object_or_404, redirect from .models import Resource def index(request): resources = Resource.objects.all().order_by('-created_at') return render(request, 'myapp/index.html', {'resources': resources})

Resource.objects.all()就是查询所有对象,.order_by('-created_at')是按创建时间倒序。这里解释一下为什么用负号:在ORM里,order_by('created_at')是升序,加个负号就是降序,相当于SQL里的ORDER BY created_at DESC

详情页视图:

def resource_detail(request, pk): resource = get_object_or_404(Resource, pk=pk) return render(request, 'myapp/detail.html', {'resource': resource})

get_object_or_404是Django提供的一个便捷函数:如果能查到对象就返回,查不到就直接抛404。如果你自己写try判空,代码会啰嗦很多,还容易漏掉异常处理。第一次作业能用好这个函数,代码质量会明显上一个台阶。

3.3 删除对象:一定用POST,别用GET

“删除对象”这个功能看着简单,但第一次作业里翻车的人特别多,原因基本都是:删除操作用了GET请求

先看正确的写法:

def resource_delete(request, pk): resource = get_object_or_404(Resource, pk=pk) if request.method == 'POST': resource.delete() return redirect('index') return redirect('index')

为什么删除必须用POST而不是GET?因为GET请求是“安全幂等”的,意味着它不应该改变服务器数据;而且GET请求可以藏在一个图片链接、一个预加载请求里,用户可能什么都没点,资源就被删了。用POST至少能保证用户通过表单提交或脚本触发才会删除。这是第一次作业里老师非常看重的一个安全意识。

前端模板里,删除按钮要放在form表单里:

<form action="{% url 'resource_delete' resource.pk %}" method="post"> {% csrf_token %} <button type="submit">删除</button> </form>

这里有个CSRF令牌,也是Django的硬性要求。如果模板form里不加{% csrf_token %},提交POST时Django会返回403。很多第一次作业的人被这个403卡住,其实就是因为忘了加这个标签。

3.4 模板渲染和静态文件:让页面看起来不那么“裸”

视图函数有了,模板也得跟上。在myapp目录下新建templates/myapp/,建议套个myapp子目录,这样模板命名不会和其他应用冲突。

列表页index.html核心代码大致这样:

<!DOCTYPE html> <html lang="zh-hans"> <head> <meta charset="UTF-8"> <title>资源列表</title> </head> <body> <h1>多媒体资源管理</h1> {% for resource in resources %} <div> <a href="{% url 'resource_detail' resource.pk %}">{{ resource.title }}</a> <span>{{ resource.created_at }}</span> </div> {% empty %} <p>暂无资源</p> {% endfor %} </body> </html>

这段模板里有几个固定语法:{% for %}是循环,{% empty %}是当列表为空时的兜底,{{ resource.title }}是输出变量的值。模板语法不算多,掌握循环、if判断、变量输出、url反向解析这四样,就足以应付第一次作业了。

静态文件(CSS、JS、图片)要放在myapp/static/myapp/目录下,然后模板顶部加{% load static %},引用时用{% static 'myapp/style.css' %}。这个在Django 4.x之后默认不需要额外配置,但要记得STATIC_URL = 'static/'这一行在settings.py里是存在的。

我自己交第一次作业时犯过一个特蠢的错误:模板里直接用了/static/myapp/style.css这种绝对路径,结果换一台机器就跑不出来了。用{% static %}标签,系统会根据STATIC_URL自动拼接,路径永远正确。

4. 加分项:文件下载与StreamingHttpResponse参数解析

如果作业标题里有“下载功能”这几个字,那恭喜你,你已经进入Django作业的加分赛段了。用StreamingHttpResponse做下载功能,代码量不大,但能体现出你对HTTP协议的理解。

4.1 StreamingHttpResponse是什么

Django返回响应,通常用的HttpResponserenderredirect。这些响应都是“一次性把内容全部生成完再发给浏览器”。但如果我们要下载一个很大的文件,一次性读进内存就会很吃紧。这时可以用StreamingHttpResponse,它把文件内容一块一块地流式发送给客户端,内存占用恒定。

注意,StreamingHttpResponse不是凭空自定义的类,它是Django内置的一个响应类。它的核心思想是:响应的内容部分可以是一个迭代器,Django会不断从迭代器里取数据并发送给浏览器。

4.2 下载视图的完整实现

以我们前面定义的Resource模型为例,文件的字段是file = models.FileField(...)。一个下载视图可以这样写:

import os from django.http import StreamingHttpResponse from django.utils.encoding import escape_uri_path from django.shortcuts import get_object_or_404 from .models import Resource def resource_download(request, pk): resource = get_object_or_404(Resource, pk=pk) file_path = resource.file.path def file_iterator(file_path, chunk_size=8192): with open(file_path, 'rb') as f: while True: chunk = f.read(chunk_size) if not chunk: break yield chunk response = StreamingHttpResponse( file_iterator(file_path), content_type='application/octet-stream' ) filename = os.path.basename(file_path) response['Content-Disposition'] = f"attachment; filename*=UTF-8''{escape_uri_path(filename)}" return response

这个视图里最关键的是后半部分,两个参数必须理解透,因为面试或者作业答辩时老师极大概率会问:

参数一:content_type

content_type是HTTP响应头里的Content-Type字段,告诉浏览器“这份内容是什么类型”。如果是application/octet-stream,浏览器一律当作二进制文件下载,不会尝试预览。如果你的资源是图片或PDF,并且想直接预览而非下载,可以设成image/pngapplication/pdf。第一次做下载功能,无脑用application/octet-stream最稳妥。

参数二:Content-Disposition

Content-Disposition是一个响应头字段,用来告诉浏览器“这个内容是内联显示(inline)还是附件下载(attachment)”。

response['Content-Disposition'] = "attachment; filename*=UTF-8''{escape_uri_path(filename)}"

这里拆开讲:

  • attachment表示作为附件下载,浏览器会弹出下载窗口;如果改成inline,部分浏览器会在当前窗口直接预览内容。
  • filename*=UTF-8''这种写法叫RFC 5987编码,是为了解决中文文件名乱码问题。Django里escape_uri_path会对文件名做URL转义,中文文件名的下载就能正常显示了。

提示:StreamingHttpResponse里设置的Content-Disposition不是构造函数参数,而是通过response['Content-Disposition'] = ...这种字典式赋值来设置响应头。很多人在这里看文档看漏了,误以为要传进构造函数里。

4.3 FileResponse vs StreamingHttpResponse:什么时候选谁

第一次作业只需要会一个就够了,但如果你想让代码更优雅,建议把FileResponse也了解一下:

特性FileResponseStreamingHttpResponse
文件加载方式自动按块读取文件流手动写迭代器按块读取
适用文件类型所有文件类型所有文件类型
代码复杂度
自定义灵活性较低高,可自定义读取方式

FileResponse本质上也是基于流式处理实现的,文件比较大时同样不会把整个文件读进内存。所以,如果只是单纯下载文件,用FileResponse更省事:

from django.http import FileResponse def resource_download(request, pk): resource = get_object_or_404(Resource, pk=pk) response = FileResponse(resource.file.open('rb'), content_type='application/octet-stream') response['Content-Disposition'] = f"attachment; filename*=UTF-8''{escape_uri_path(os.path.basename(resource.file.name))}" return response

我个人建议:第一次作业优先掌握StreamingHttpResponse,因为答辩时你更容易把“为什么用流式响应、参数怎么设置”讲清楚。FileResponse可以当作进阶内容提一提,显得你不止会照抄代码。

5. 后台管理界面美化:不用大改,这几点就能让老师眼前一亮

很多第一次作业要求“后台管理”,默认的Django admin已经够用了。但默认admin的界面比较朴素,如果你想让作业看起来更用心,有两条路:一是用第三方库,二是手动设置admin类。

5.1 admin.py里配置模型的管理选项

打开myapp/admin.py,不满足于简单注册,可以用ModelAdmin细化展示:

from django.contrib import admin from .models import Resource @admin.register(Resource) class ResourceAdmin(admin.ModelAdmin): list_display = ('id', 'title', 'created_at') list_filter = ('created_at',) search_fields = ('title', 'description') ordering = ('-created_at',) list_per_page = 10

这些配置选项的作用分别是:

  • list_display:列表页显示哪些字段。默认只显示__str__方法返回的内容,有了这个配置,老师一眼扫过去就能看到标题和创建时间,不用点进每条详情。
  • list_filter:右侧出现筛选栏,可以按创建时间过滤。
  • search_fields:顶部出现搜索框,按标题和描述检索。
  • ordering:列表默认排序方式。
  • list_per_page:每页显示条数,数据多了以后不用一页拉到底。

第一次作业能把这五个选项配好,管理员后台的可用性会有一个明显的提升。说实话,list_display这条是最好用也最容易印象分的,很多老师打开后台看到整整齐齐的表格,第一印象就好。

5.2 想要更美观:简单提一下simpleui

第三方库simpleui可以快速美化Django admin,安装和配置很简单:

pip install django-simpleui

然后在INSTALLED_APPS里,把'simpleui'放在'django.contrib.admin'前面。重新启动服务,后台界面就会变成一套现代化的后台风格。

不过这里要提醒一句:第一次作业用不用simpleui看情况。如果作业要求是“不要用任何第三方插件,展示原生Django能力”,那用了反而画蛇添足。如果是综合实战类的“XX管理系统”,用simpleui就是加分项。我先讲清楚利弊,你自己判断。

5.3 给admin换上一个中文标题

这算是性价比最高的“美化”手段。在admin.py里加两行:

admin.site.site_header = '多媒体资源管理后台' admin.site.site_title = '资源管理'

改完之后刷新后台,页头显示的就是中文标题,看上去更像一个“完整系统”,而不是Django的默认demo。我第一次作业就是这么干的,老师当时说“一看就是做过需求分析的”,其实我只是改了两行配置。

6. 交作业前必须排查的常见问题清单

第一次作业的代码写完之后,最怕的不是功能没写完,而是交上去以后在老师机器上跑不起来。下面这些坑我基本都踩过,列一个排查表,你写完之后对着过一遍,能省下很多沟通成本。

6.1 环境与依赖问题

现象常见原因解决办法
提示No module named 'django'虚拟环境没激活,或者项目所需的Django没安装检查命令行是否有(venv)前缀;执行pip install django
提示No module named 'MySQLdb'mysqlclient没装好参考2.2节几种方式装好,或使用PyMySQL
老师那边跑不起来缺少依赖清单pip freeze > requirements.txt把依赖导出,随代码一起提交
端口被占用8000端口被其他进程占用换端口:python manage.py runserver 8001

6.2 迁移与数据库问题

现象常见原因解决办法
makemigrations提示无变化models.py没有保存,或应用没注册到INSTALLED_APPS检查应用是否在INSTALLED_APPS里,确认models.py有改动
migrateUnknown databaseMySQL里还没有创建对应数据库手动执行CREATE DATABASE
新增字段后后台看不到忘记迁移重新执行makemigrationsmigrate
中文乱码数据库字符集不是utf8mb4建库时指定utf8mb4;settings里配置OPTIONS.charset

6.3 页面与功能问题

现象常见原因解决办法
打开页面500模板语法错误、视图里变量名不对看终端里的完整报错堆栈,Django报错信息很详细
删除按钮没反应模板里没有{% csrf_token %}, 或请求方法不是POST检查form里是否包含csrf_token,method是否为post
静态文件加载不出来模板缺少{% load static %}或路径引用错误{% static 'myapp/style.css' %}代替绝对路径
中文文件名下载乱码Content-Disposition没有做编码处理escape_uri_path处理文件名,filename*=UTF-8''
列表页显示的是“Resource object (1)”__str__方法没写在models.py里加上def __str__(self)返回有意义的字段

6.4 一个很实用的排查技巧

python manage.py runserver的时候,如果页面报错,终端里打印的堆栈就是最直接的线索。Django调试模式下的错误页面会显示报错文件、行号和具体异常信息,第一次作业阶段遇到报错不要慌,先看最下面那行异常类型,再往上看代码位置,绝大多数问题都能自己解决。

另外,把DEBUG = True留在settings.py里是对付作业期的最好办法。如果项目上线,这个必须改成False,但交作业阶段保持True,老师还能在页面上看到详细的调试信息,方便你演示时排查问题。等作业验收完,再聊聊安全性和部署也不迟。

一个小建议:把作业当作自己第一个“作品”来对待

第一次Django作业做完了,不管代码写得多简洁,我觉得最有价值的不是分数,而是你理解了“一个Web项目从无到有是怎么串起来的”。从环境搭建、数据库设计、ORM操作、URL路由、视图函数到模板渲染,这套流程以后做任何Django项目都是同一个骨架。

我个人实际操作中的体会是:第一次交作业时,代码不怕简单,就怕“没有逻辑地抄”。如果能把每个文件的作用、每次迁移的本质、每个HTTP参数的含义都讲清楚,哪怕功能少一点,老师也会觉得你是真的掌握了这个框架。反过来,功能堆了一堆但一问三不知,反而容易露馅。

最后再分享一个小技巧:交作业前,把项目目录下的venv删掉或者注明“运行时需重新创建虚拟环境”,不要把虚拟环境打包提交。另外在项目根目录放一个README.md,写上运行步骤(激活环境、安装依赖、迁移、创建管理员、启动),这个小文件能让你省下大量解释时间,亲测有效。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询