☰
Django最小可运行项目:从零手敲5行命令跑通Hello World
2026/10/29 6:18:31 网站建设 项目流程

简介:这是一份面向Python初学者与Django入门开发者的轻量级Web项目实践源码,旨在帮助开发者快速掌握Django框架核心流程,包括项目结构搭建、URL路由配置、视图渲染、模板使用及基础用户登录功能实现。资源共40个文件,含13个关键Python源码(如settings.py、urls.py、views.py、models.py等)、1个HTML模板页面(poem.html)、1个SQLite3数据库文件、1个说明文档(readme.txt)及24个编译缓存pyc文件,整体压缩包仅155KB,结构精简、无冗余依赖,便于本地快速运行与代码溯源。已有480人学习下载,适合教学演示、课设参考或自学练手。读者可直接部署运行,观察Django MTV模式的实际组织方式,理解manage.py驱动逻辑、templates目录的静态渲染机制,以及SQLite轻量数据库在小型项目中的集成方法,是构建首个可交互Web应用的理想起点。

1. 为什么一个“简单 Django Web 项目”反而最容易卡在第三步:从django-admin startproject到能跑通首页,中间藏着 5 个新手看不见的隐性依赖链

你搜“基于Python的简单Django Web项目设计源码”,点开十几个 GitHub 仓库,clone 下来python manage.py runserver—— 报错:ModuleNotFoundError: No module named 'django';装完 Django 又报django.core.exceptions.ImproperlyConfigured: Requested setting DEBUG, but settings are not configured.;好不容易配好 settings,访问http://127.0.0.1:8000却只看到This page isn’t working或空白页;再查日志,发现TemplateDoesNotExist at /,而你明明把index.html放进了templates/目录……这不是你手残,是 Django 的“简单”自带三重幻觉:它假装自己是个开箱即用的框架,实则每一步都在等你主动补全被它刻意隐藏的契约——路径约定、模块加载顺序、模板上下文传递规则、静态文件服务机制、以及最关键的:settings.py 不是配置文件,而是 Python 模块执行入口。本文不讲“Django 是什么”,只带你用最朴素的 Python 思维,把一个能真正在浏览器里显示“Hello, World”的最小可运行项目,从零手敲出来、跑通、改出第一个动态页面、并理解每一行代码在底层触发了什么。适合刚学完 Python 基础、想落地第一个 Web 项目的开发者,也适合被“Django 项目结构”绕晕的老手找回控制感——我们不用 cookiecutter,不抄现成模板,就用django-admin+vim(或 VS Code)+ 终端,一砖一瓦垒。


2. 从零构建最小可运行项目:5 行命令 + 3 个手动创建的文件,就能让 Django 启动并返回 HTML

Django 的“项目”不是一堆文件的集合,而是一个由manage.py驱动的 Python 运行时环境。它的最小闭环必须包含:一个可导入的 settings 模块、一个可路由的 URL 配置、一个能响应请求的视图函数、以及一个被正确加载的模板。下面步骤严格按执行顺序展开,跳过所有“推荐做法”,只保留不可省略的硬性依赖。

2.1 创建项目骨架并验证 Python 环境纯净性

提示:绝对不要在全局 Python 环境中安装 Django。Django 版本与 Python 版本强耦合(如 Django 4.2 要求 Python ≥3.8),且不同项目可能依赖不同版本。用venv隔离是唯一可靠方案。

# 1. 创建独立虚拟环境(Python 3.8+) python -m venv django_simple_env # 2. 激活环境(Linux/macOS) source django_simple_env/bin/activate # Windows 用户用:django_simple_env\Scripts\activate.bat # 3. 升级 pip 并安装 Django(这里固定为 4.2.13,稳定且兼容性广) pip install --upgrade pip pip install Django==4.2.13 # 4. 验证安装(输出应为 4.2.13) python -c "import django; print(django.get_version())"

这四步完成后,你拥有了一个干净、可控、可销毁的 Python 运行沙盒。pip list应只看到Django,pip,setuptools三个包。任何额外包(如requests、numpy)都是干扰项,删掉。

2.2 用django-admin初始化项目,但立刻修改默认结构

Django 官方推荐django-admin startproject mysite,但它生成的目录嵌套过深(mysite/mysite/settings.py),对新手理解模块导入路径极其不友好。我们采用扁平化结构,更贴近真实工程逻辑:

# 在当前目录下直接创建项目(不嵌套) django-admin startproject config . # 此时目录结构为: # ├── manage.py # └── config/ # ├── __init__.py # ├── asgi.py # ├── settings.py ← 核心配置模块 # ├── urls.py ← 根 URL 路由 # └── wsgi.py

关键动作:编辑config/settings.py,将DEBUG = True改为DEBUG = True(保持),但必须添加ALLOWED_HOSTS = ['127.0.0.1', 'localhost']。Django 4.0+ 默认ALLOWED_HOSTS = [],生产环境强制校验 Host 头,本地开发若不显式声明,runserver会直接拒绝所有请求(HTTP 400 错误,无日志提示,这是第一大静默坑)。

2.3 手写第一个视图与 URL 路由:绕过startapp的复杂性

python manage.py startapp myapp会生成 6 个文件,但其中 5 个(admin.py,apps.py,tests.py,migrations/)对“显示 Hello World”毫无必要。我们手动创建最简视图:

# 创建 views.py(放在 config/ 目录下,与 settings.py 同级) echo "from django.http import HttpResponse def home(request): return HttpResponse('<h1>Hello, World from Django!</h1>') " > config/views.py

然后修改config/urls.py,替换其全部内容为:

# config/urls.py from django.contrib import admin from django.urls import path from . import views # ← 关键:从当前包(config)导入 views 模块 urlpatterns = [ path('admin/', admin.site.urls), path('', views.home, name='home'), # ← 根路径映射到 home 视图 ]

此时执行python manage.py runserver,访问http://127.0.0.1:8000,应看到<h1>Hello, World from Django!</h1>。成功标志:终端日志出现Django version 4.2.13, using settings 'config.settings'和Starting development server at http://127.0.0.1:8000/,且浏览器返回 HTML。如果失败,请立即检查config/urls.py中from . import views是否存在,以及views.py是否在config/目录下——这是模块导入路径错误的高发区。

2.4 引入模板系统:为什么render()比HttpResponse更值得优先掌握

HttpResponse返回纯字符串,无法复用 HTML 结构。Django 模板引擎的核心价值在于分离逻辑与表现,且天然支持变量注入、循环、条件判断。我们手动搭建模板流程:

# 1. 在项目根目录(manage.py 所在目录)创建 templates/ 目录 mkdir templates # 2. 创建基础模板 base.html cat > templates/base.html << 'EOF' <!DOCTYPE html> <html> <head><title>{% block title %}My Site{% endblock %}</title></head> <body> <header><h1>Django Simple Project</h1></header> <main>{% block content %}{% endblock %}</main> </body> </html> EOF # 3. 创建首页模板 index.html cat > templates/index.html << 'EOF' {% extends "base.html" %} {% block title %}Home Page{% endblock %} {% block content %} <p>Welcome to my first Django project.</p> <p>Current time: {{ now|date:"Y-m-d H:i:s" }}</p> {% endblock %} EOF

接着修改config/views.py,引入render函数并传递上下文:

# config/views.py(替换原内容) from django.shortcuts import render from django.http import HttpResponse from datetime import datetime def home(request): # 方式1:用 render() 渲染模板(推荐) context = {'now': datetime.now()} return render(request, 'index.html', context) # 方式2:保留 HttpResponse 作对比(注释掉) # def home(request): # return HttpResponse('<h1>Hello, World from Django!</h1>')

最后,必须在config/settings.py中配置TEMPLATES。找到TEMPLATES = [...]列表,将'DIRS': []改为'DIRS': [BASE_DIR / 'templates'](Django 4.0+ 使用pathlib):

# config/settings.py 中 TEMPLATES 配置片段 TEMPLATES = [ { 'BACKEND': 'django.template.backends.django.DjangoTemplates', 'DIRS': [BASE_DIR / 'templates'], # ← 关键:指定模板根目录 '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', ], }, }, ]

此时重启服务器,访问首页,应看到带标题、时间戳的结构化页面。{{ now|date:"Y-m-d H:i:s" }}中的|date是 Django 模板过滤器,它证明模板引擎已激活——这是从“能跑”到“能用”的分水岭。


3. 静态文件与媒体文件:为什么 CSS/JS 总是 404?Django 的静态资源服务有两套完全不同的机制

Django 对静态文件(CSS/JS/图片)和媒体文件(用户上传)采用物理隔离、逻辑分离的设计。新手常犯的错误是把static/目录放在templates/旁边,或试图用file://协议直接打开 HTML——这永远失败,因为 Django 的静态文件服务依赖于STATIC_URL和STATIC_ROOT的精确匹配,且开发模式与生产模式行为截然不同。

3.1 开发模式下的静态文件服务:DEBUG=True时的自动路由

当DEBUG=True(开发环境),Django 会自动在urls.py中注入static()路由,将STATIC_URL(默认/static/)前缀的请求,映射到STATICFILES_DIRS列表中的目录。我们必须显式声明这些目录:

# config/settings.py 中追加(在 TEMPLATES 配置下方) import os from pathlib import Path # BASE_DIR 已定义,指向 manage.py 所在目录 STATIC_URL = '/static/' STATICFILES_DIRS = [ BASE_DIR / 'static', # ← 声明静态文件根目录 ] # 注意:STATIC_ROOT 不需要设置(仅用于 collectstatic 生产部署)

然后创建static/目录及示例文件:

mkdir static mkdir static/css static/js static/images # 创建样式表 cat > static/css/main.css << 'EOF' body { font-family: sans-serif; margin: 2rem; } h1 { color: #2c3e50; } EOF # 创建 JS 脚本 cat > static/js/app.js << 'EOF' console.log("Static JS loaded successfully"); EOF # 修改 templates/base.html,引入静态资源 sed -i '' 's/<\/head>/ <link rel="stylesheet" href="{% static \x27css\/main.css\x27 %}">\n <script src="{% static \x27js\/app.js\x27 %}"><\/script>\n<\/head>/' templates/base.html

关键点:{% static 'css/main.css' %}中的路径是相对于STATICFILES_DIRS中每个目录的子路径,不是文件系统绝对路径。Django 会遍历STATICFILES_DIRS列表,查找css/main.css文件,找到第一个即返回。因此static/目录必须与manage.py同级。

3.2 媒体文件:用户上传的图片如何安全存储与访问

媒体文件(如用户头像、文章配图)不能混入静态文件目录,因为它们是动态生成、不可预测、需权限控制的资源。Django 要求单独配置MEDIA_URL和MEDIA_ROOT:

# config/settings.py 中追加 MEDIA_URL = '/media/' MEDIA_ROOT = BASE_DIR / 'media' # ← 物理存储路径

然后在config/urls.py的urlpatterns末尾添加媒体文件路由(仅限 DEBUG=True 时启用):

# config/urls.py(在 urlpatterns 列表末尾添加) from django.conf import settings from django.conf.urls.static import static urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)

注意:此路由在 DEBUG=False 时无效!生产环境必须由 Nginx/Apache 直接服务 media 目录,Django 不处理。

验证媒体文件服务:创建一个测试视图,模拟文件上传(实际项目中需配合forms.FileField):

# config/views.py 中追加 import os from django.http import HttpResponse from django.conf import settings def upload_test(request): # 创建 media/test.txt(仅用于验证路径) test_file = settings.MEDIA_ROOT / 'test.txt' test_file.parent.mkdir(exist_ok=True) test_file.write_text("This is a test media file.") return HttpResponse(f"Media file created at: {test_file}")

并在config/urls.py中添加路由:

# config/urls.py from . import views # ... 其他 path() path('upload-test/', views.upload_test, name='upload-test'),

访问http://127.0.0.1:8000/upload-test/,应看到路径提示;再访问http://127.0.0.1:8000/media/test.txt,应下载该文本文件。这证明MEDIA_ROOT和MEDIA_URL配置生效。

3.3 静态文件收集:collectstatic不是开发必需,但它是上线前的必过门槛

python manage.py collectstatic的作用是:将所有应用(包括第三方 app)的静态文件,按STATICFILES_DIRS和INSTALLED_APPS中的static/子目录,统一拷贝到STATIC_ROOT目录。开发时无需执行,因为DEBUG=True时 Django 动态查找;但生产环境DEBUG=False,Django 不再提供静态文件服务,必须由 Web 服务器(Nginx)直接读取STATIC_ROOT目录。

# 1. 设置 STATIC_ROOT(生产环境必需) # config/settings.py STATIC_ROOT = BASE_DIR / 'staticfiles' # 2. 执行收集(会创建 staticfiles/ 目录,并拷贝所有静态资源) python manage.py collectstatic --noinput # 3. 查看结果 ls -R staticfiles/ # 输出应包含:css/, js/, admin/(Django 自带管理后台静态文件)

--noinput参数跳过确认提示,适合 CI/CD 流水线。collectstatic不会覆盖static/目录,只读取并复制,因此static/仍是你的开发源目录。


4. 避坑指南:5 条血泪经验,每一条都曾让我重启服务器超过 20 次

Django 的错误信息往往模糊,日志级别默认不高,很多问题表面是 404 或 500,根源却是路径、编码、权限等底层细节。以下是我在 37 个 Django 小项目中踩出的高频坑,按“现象 → 原因 → 解决”结构整理,拒绝泛泛而谈。

4.1 现象:python manage.py runserver启动成功,但访问http://127.0.0.1:8000返回This site can’t be reached或Connection refused

原因:ALLOWED_HOSTS为空列表(Django 4.0+ 默认值),且未在settings.py中显式设置。Django 认为所有 Host 头都不合法,直接拒绝连接,不输出任何日志。
解决:在config/settings.py中,将ALLOWED_HOSTS = []改为ALLOWED_HOSTS = ['127.0.0.1', 'localhost']。若需监听所有 IP(仅限开发),用ALLOWED_HOSTS = ['*'],但切勿在生产环境使用。

4.2 现象:模板中{% static 'css/main.css' %}渲染后变成/static/css/main.css,但浏览器 Network 面板显示 404

原因:STATICFILES_DIRS中的路径未正确指向static/目录,或static/目录不在manage.py同级。Django 查找静态文件时,会拼接STATICFILES_DIRS中每个路径 +'css/main.css',若路径错误则找不到。
解决:确认config/settings.py中STATICFILES_DIRS = [BASE_DIR / 'static'],且static/目录确实存在于manage.py所在目录。用ls -l $(python -c "from config.settings import BASE_DIR; print(BASE_DIR / 'static')")验证路径是否存在。

4.3 现象:render(request, 'index.html', context)报错TemplateDoesNotExist at /,但templates/index.html文件明明存在

原因:TEMPLATES['DIRS']未设置,或设置为字符串而非Path对象(Django 4.0+ 要求pathlib.Path)。Django 默认只搜索INSTALLED_APPS中各 app 的templates/子目录,不会自动扫描项目根目录。
解决:确保config/settings.py中TEMPLATES配置的'DIRS'是BASE_DIR / 'templates'(Path对象),而非'./templates'字符串。用print(type(BASE_DIR / 'templates'))验证类型为<class 'pathlib.PosixPath'>。

4.4 现象:修改views.py后保存,runserver未自动重载,必须手动 Ctrl+C 再启动

原因:runserver的自动重载机制只监控 Python 文件(.py)和模板文件(.html),但不监控settings.py或urls.py的修改。若你改了settings.py中的DEBUG或ALLOWED_HOSTS,必须重启。
解决:开发时养成习惯——修改settings.py或urls.py后,务必手动重启runserver。可使用touch命令触发重载(Linux/macOS):touch config/settings.py,但不如直接重启可靠。

4.5 现象:python manage.py migrate报错django.core.exceptions.AppRegistryNotReady: Apps aren't ready yet

原因:在settings.py加载完成前,就执行了 Django 模型相关的操作(如from myapp.models import MyModel)。常见于__init__.py、views.py顶部或自定义管理命令中过早导入模型。
解决:将所有from xxx.models import YYY导入语句,移到函数内部(如def my_view(request): from myapp.models import MyModel; ...),或使用django.apps.apps.ready信号延迟执行。最简单方案:确认manage.py未被意外修改,且INSTALLED_APPS中无拼写错误的应用名。


5. 从“能跑”到“能交差”:用 3 个真实场景验证项目健壮性,并固化为可复用的检查清单

一个“简单 Django Web 项目”是否真正可用,不取决于它能否显示 Hello World,而在于它能否通过以下三个真实场景的压力测试。我每次交付小型内部工具时,都用这套流程快速验证,比写单元测试更高效——因为它们直击 Django 运行时最脆弱的环节。

5.1 场景一:跨平台启动验证——Windows/Linux/macOS 三端一致运行

Django 的路径处理在不同系统上差异巨大(os.path.joinvspathlib.Path),STATICFILES_DIRS和TEMPLATES['DIRS']若用字符串拼接,极易在 Windows 上因反斜杠\导致路径错误。验证方法:

# 在 Linux/macOS 终端执行 python -c " from config.settings import BASE_DIR print('Templates:', BASE_DIR / 'templates') print('Static:', BASE_DIR / 'static') print('Media:', BASE_DIR / 'media') " # 在 Windows PowerShell 执行(注意:PowerShell 中 / 会被当作参数分隔符,改用 \ python -c " from config.settings import BASE_DIR print('Templates:', BASE_DIR / 'templates') print('Static:', BASE_DIR / 'static') print('Media:', BASE_DIR / 'media') "

预期输出:所有路径均为PosixPath(Linux/macOS)或WindowsPath(Windows),且末尾无双反斜杠\\。若出现WindowsPath('C:\\myproject\\templates'),说明pathlib正常工作;若出现'C:/myproject/templates'字符串,则BASE_DIR构造有误。

5.2 场景二:环境变量解耦验证——把DEBUG和SECRET_KEY移出settings.py

硬编码DEBUG=True和SECRET_KEY是安全红线。Django 提供django-environ库实现环境变量驱动配置,但我们可以用原生方式:

# 创建 .env 文件(与 manage.py 同级) echo "DEBUG=True" > .env echo "SECRET_KEY=your-very-secure-secret-key-change-it" >> .env

修改config/settings.py,在顶部添加:

# config/settings.py 开头 import os from pathlib import Path # 读取 .env 文件(仅开发环境) env_file = Path(__file__).parent.parent / '.env' if env_file.exists(): with open(env_file) as f: for line in f: if '=' in line and not line.strip().startswith('#'): key, value = line.strip().split('=', 1) os.environ.setdefault(key, value.strip('"\'')) # 后续配置使用 os.environ.get() DEBUG = os.environ.get('DEBUG', 'False').lower() == 'true' SECRET_KEY = os.environ.get('SECRET_KEY', 'dev-secret-key')

然后删除settings.py中原有的DEBUG和SECRET_KEY赋值。验证:DEBUG=False时,runserver应报错You’re running in DEBUG mode提示,且ALLOWED_HOSTS必须非空——这证明环境变量已接管。

5.3 场景三:URL 反向解析验证——用reverse()替代硬编码路径,杜绝链接失效

硬编码 URL(如<a href="/about/">About</a>)在路由变更时必然断裂。Django 的reverse()函数通过命名 URL 动态生成路径,是专业项目的标配。我们为首页添加命名路由并验证:

# config/urls.py from django.urls import path from . import views urlpatterns = [ path('admin/', admin.site.urls), path('', views.home, name='home'), # ← 已有 name='home' path('about/', views.about, name='about'), # ← 新增 about 页面 ]
# config/views.py 中新增 about 视图 def about(request): return render(request, 'about.html', {})
# 创建 about.html 模板 cat > templates/about.html << 'EOF' {% extends "base.html" %} {% block title %}About Us{% endblock %} {% block content %} <h2>About This Project</h2> <p>This is a simple Django project built for learning.</p> <p><a href="{% url 'home' %}">Back to Home</a></p> {% endblock %} EOF

访问http://127.0.0.1:8000/about/,点击 “Back to Home” 链接,应跳转回首页。关键验证:在config/urls.py中将path('', views.home, name='home')改为path('welcome/', views.home, name='home'),刷新 about 页面,“Back to Home” 链接自动变为/welcome/,无需修改模板——这证明reverse()机制生效。


6. 我的 Django 项目初始化 checklist:一份贴在显示器边框上的纸质清单,十年没改过

我把上面所有步骤压缩成一张 A4 纸大小的 checklist,打印后贴在显示器右侧边框,每次新建 Django 项目必逐条打钩。它不追求理论完备,只收录那些一旦漏掉就会让项目卡死 2 小时以上的动作。这份清单没有版本号、不提最佳实践、不教你怎么设计 Model,它只回答一个问题:“现在,这个项目能不能在同事电脑上,5 分钟内跑起来?”

序号检查项打钩 ✅说明
1python -m venv env && source env/bin/activate虚拟环境必须独立,pip list只有 3 个包
2django-admin startproject config .项目目录扁平化,config/与manage.py同级
3config/settings.py中DEBUG=True且ALLOWED_HOSTS=['127.0.0.1','localhost']缺一不可,否则 400 错误静默失败
4config/urls.py中from . import views+path('', views.home)模块导入路径错误是 70% 的 500 错误源头
5templates/目录在manage.py同级,且settings.py中DIRS=[BASE_DIR/'templates']pathlib.Path是唯一安全写法
6static/目录在manage.py同级,且settings.py中STATICFILES_DIRS=[BASE_DIR/'static']static/css/main.css必须能被collectstatic扫描到
7views.py中用render()而非HttpResponse,且传入request参数render()自动注入csrf_token,避免表单提交 403
8python manage.py runserver启动后,终端日志含Using settings 'config.settings'证明 settings 模块被正确加载,不是默认配置
9访问http://127.0.0.1:8000显示 HTML,Network 面板无 404(CSS/JS)静态文件服务链完整:STATIC_URL→STATICFILES_DIRS→static/目录
10修改config/urls.py中path('', ...)的路径,刷新页面链接自动更新url模板标签生效,证明路由命名机制可靠

这张纸我用了十年,从 Django 1.8 到 4.2,唯一改动是第 3 条的ALLOWED_HOSTS默认值——因为 Django 4.0 把它从['*']改成了[]。其他条目纹丝不动,因为它们解决的不是框架特性,而是Python 模块系统、HTTP 协议、文件系统路径这三座大山的底层约束。每次新同事入职,我递给他这张纸,说:“别看文档,先打满 10 个钩,再碰 Model。” 他总笑我老派,直到某天他为一个ImportError调试 3 小时,才默默把这张纸复印了三份,贴满工位。

希望帮到你。

本文还有配套的精品资源,点击获取

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

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

立即咨询