简介:一个开箱即用的Django入门项目,专注传统服务端渲染流程,不引入任何前端框架。通过简单的URL配置(urls.py)将请求分发到视图函数(views.py),视图调用calculate.py完成基础业务计算,并把结果连同上下文数据传入hello.html和.html等HTML模板,由Django内置模板引擎完成最终渲染。项目结构完整,包含标准Django应用组件:models.py定义数据模型,admin.py支持后台管理,apps.py声明应用配置,migrations目录管理数据库迁移,templates文件夹集中存放所有HTML模板,settings.py提供调试与部署相关设置,manage.py用于常规命令操作,wsgi.py和asgi.py分别适配WSGI/ASGI服务器部署。配套SQLite数据库(db.sqlite3)已初始化,虚拟环境(venv)和开发配置(.gitignore、.idea、pycache)均已就位,不影响运行。适合新手动手实践请求生命周期、URL映射、视图响应、模板变量与标签、静态资源组织等核心Web开发环节。
1. 项目概述:为什么一个“过时”的服务端渲染示例,反而成了新手最该啃下的第一块硬骨头?
你点开这个项目,看到 hello.html、urls.py、views.py 这些文件名,可能第一反应是:“这不就是十年前的老古董吗?现在谁还用 Django 直接 render HTML?不都上 Vue + REST API 了?”——我第一次带实习生时,也听到过一模一样的质疑。但后来我们花了整整三周,把一个本该两天搞定的后台管理页,反复推倒重来:先用 Vue 写一遍,接口调不通、状态错乱、跨域头疼;再用纯 Django 模板写一遍,{{ result }} 一行就显示计算结果,{% if user.is_authenticated %} 两行搞定权限判断,{% for item in items %} 循环列表干净利落。最后实习生盯着浏览器里那个没有 JS 报错、没有网络请求瀑布流、却稳稳当当跑起来的页面,说了句:“原来‘简单’不是功能少,而是逻辑全在眼皮底下。”
这就是这个项目存在的全部意义:它不炫技,不堆栈,不制造抽象层,它把 Web 开发最底层的“请求-响应”链条,像拆解一台机械钟表一样,一颗齿轮、一根游丝地摊在你面前。关键词里写的 Django模板、服务端渲染、视图函数、路由配置、HTML渲染,每一个都不是孤立概念,而是一条环环相扣的流水线——用户敲下回车,DNS 解析、TCP 握手、HTTP 请求抵达 Django 的 urls.py(这是第一道闸门),它根据正则或路径匹配,把请求“扔”给 views.py 里的某个函数(这是第二道闸门),这个函数调用 calculate.py 做加减乘除(这是业务核心),再把数字、字符串、列表一股脑塞进字典,传给 render() 函数(这是第三道闸门),render() 拿着这个字典,打开 hello.html,找到 {{ name }} 就替换成字典里的值,遇到 {% for %} 就循环渲染 HTML 片段,最后把拼好的完整 HTML 字符串,通过 HTTP 响应体,原封不动吐给浏览器(这是最后一道闸门)。整个过程,没有 AJAX,没有 JSON.parse,没有虚拟 DOM diff,只有 Python 代码和 HTML 字符串之间最朴素的“填空游戏”。
所以它适合谁?不是适合想立刻上线 SaaS 产品的创业者,而是适合那些刚学完 Python 基础语法、对着 print("Hello World") 欢呼雀跃,却对“网页怎么动起来”毫无头绪的新手;是适合那些被 React 的 useState 和 useEffect 绕晕、搞不清“数据驱动视图”到底驱动了什么的转行者;是适合那些在公司维护着十年老后台、天天改 admin.py 却从没自己写过 urls.py 的“半路出家”运维。它不承诺让你成为全栈大神,但它能确保你亲手拧紧每一颗螺丝后,清楚知道这台机器为什么能转,以及哪颗螺丝松了会发出异响。接下来,我们就从这条流水线的起点开始,一节一节地拆解、复现、踩坑、验证。
2. 整体架构与设计思路:为什么坚持“传统”,而不是拥抱“现代”?
2.1 核心设计哲学:拒绝抽象,拥抱可见性
这个项目的骨架,严格遵循 Django 官方文档里最经典的“Polls Tutorial”范式,但做了更极致的简化与聚焦。它的目录结构不是为了展示“企业级应用该长什么样”,而是为了回答一个最根本的问题:“当我在浏览器地址栏输入 http://127.0.0.1:8000/calculate/ 并按下回车,Django 内部到底发生了什么?” 因此,所有组件的存在,都服务于这个单一目标:
urls.py是唯一的“交通指挥中心”,它不玩花哨的path()和re_path()混搭,只用最基础的path('calculate/', views.calculate_view, name='calculate'),清晰表明“这个 URL 路径,必须交给views.py里的calculate_view函数处理”。没有include(),没有子路由,避免新手在blog/urls.py和根urls.py之间迷失。views.py是纯粹的“业务调度员”,里面只有两个函数:home_view(负责渲染首页hello.html)和calculate_view(负责接收表单、调用计算、渲染结果页result.html)。它不做任何数据库操作(models.py是空的,仅作占位),不引入第三方库,所有逻辑都在calculate.py里,保证视图层极度轻量。calculate.py是独立的“计算器模块”,它被设计成一个可测试、可替换的纯函数集合。比如def add(a: float, b: float) -> float:,输入两个数,返回一个数,没有任何副作用。这样做的好处是,当你未来想把它换成调用外部 API 或读取数据库时,只需修改这一小块,views.py完全不用动。templates/目录下只有两个 HTML 文件:hello.html(带一个表单,提交到/calculate/)和result.html(显示计算结果)。它们不使用任何 Bootstrap 或 Tailwind CSS,只用最原始的<h1>、<p>、<form>标签,连内联样式都刻意避免,强迫你把注意力放在 Django 模板语法本身——{{ result }}怎么显示变量,{% if result > 100 %}怎么做条件判断,{% for step in steps %}怎么循环渲染步骤说明。
这种“反潮流”的设计,其底层逻辑是:新手最大的障碍,从来不是技术本身的复杂度,而是“不可见性”带来的失控感。 当你用 Vue 写一个按钮点击事件,背后是事件总线、响应式系统、虚拟 DOM 渲染三重抽象;而在这个项目里,点击按钮 → 提交表单 → 触发 calculate_view → 执行 add() → 返回 result.html → 浏览器显示 {{ result }},每一步都对应着一个你能在文件系统里直接打开、编辑、打断点的 Python 或 HTML 文件。这种“所见即所得”的确定性,是建立信心的第一块基石。
2.2 关键技术选型背后的“为什么”
为什么用 SQLite 而不是 PostgreSQL?因为新手不需要折腾数据库安装、用户权限、连接池配置。db.sqlite3 就是一个文件,双击就能用文本编辑器打开看里面存了啥(虽然内容是二进制,但至少你知道它就在那儿)。settings.py 里 DATABASES 配置只有四行,删掉注释后只剩两行:
'DATABASES': {
'default': {
'ENGINE': 'django.db.backends.sqlite3',
'NAME': BASE_DIR / 'db.sqlite3',
}
}
没有密码、没有端口、没有主机名,零配置启动。
为什么 models.py 是空的?因为本项目的核心教学目标是“渲染流程”,而非“数据建模”。引入模型意味着要学 makemigrations、migrate、ModelForm,这些都会把焦点从“HTML 怎么被填满”转移到“数据库表怎么创建”。等你彻底搞懂 render() 怎么把字典变成 HTML 后,再往里加一个 CalculationHistory 模型记录每次计算,就是水到渠成的事。
为什么 static/ 目录不存在?因为本项目刻意剥离了“静态资源管理”这个独立议题。所有 CSS 和 JS 都以内联方式写在 HTML 的 <style> 和 <script> 标签里(如果需要的话),或者干脆不用。这样,你就不会在 STATIC_URL、STATICFILES_DIRS、collectstatic 这些概念里打转。记住,一个清晰的教学示例,必须敢于做减法,而不是堆砌所有“应该有”的东西。
2.3 与“现代”全栈方案的本质区别
很多人误以为“服务端渲染”就是“落后”,其实它是一种截然不同的架构哲学。我们可以用一个生活化的类比来理解:
-
Vue/React SPA(单页应用) 就像一家“自助餐厅”:你第一次进门,厨房(服务器)给你一份完整的菜单(HTML + JS bundle),然后你所有的点餐、取餐、结账动作,都在这个菜单页面内完成。厨房只负责提供食材(API 数据),不参与你的用餐流程。优点是流畅、交互强;缺点是第一次加载慢,SEO 不友好,调试时你得在浏览器控制台和后端日志之间来回切换。
-
Django 服务端渲染 则像一家“传统中餐馆”:你点菜(发送 HTTP 请求),厨师(Django 视图)根据你的要求(URL 和参数),现场炒一道新菜(生成新的 HTML),装盘(HTTP 响应)后端给你。每一道菜都是独立的、完整的、即拿即食的。优点是首屏快、SEO 天然好、逻辑集中、调试直观;缺点是每次交互都要刷新页面,大规模复杂交互不如 SPA 灵活。
这个项目选择后者,不是因为它更好,而是因为它更透明、更易教、更易学。它把“Web 是什么”这个问题,用最朴实的方式回答了:Web 就是客户端(浏览器)和服务器(Django)之间,基于 HTTP 协议,不断交换 HTML 文本的过程。其他一切,都是在这个基础上的优化与延伸。
3. 核心细节解析与实操要点:从 urls.py 到 hello.html 的逐行拆解
3.1 路由配置 (urls.py):URL 是如何被“翻译”成 Python 函数的?
Django 的路由系统,本质上是一个“URL 字符串”到“Python 可调用对象”的映射字典。urls.py 就是这个字典的声明文件。我们来看项目中最关键的一行:
# urls.py
from django.contrib import admin
from django.urls import path
from blog import views # 注意:这里导入的是 blog 应用下的 views 模块
urlpatterns = [
path('admin/', admin.site.urls),
path('', views.home_view, name='home'), # 根路径,指向 home_view
path('calculate/', views.calculate_view, name='calculate'), # 计算路径,指向 calculate_view
]
这段代码的执行逻辑,远比表面看起来深刻。当你访问 http://127.0.0.1:8000/calculate/ 时,Django 的 URLResolver 会拿着这个字符串,从上到下依次匹配 urlpatterns 列表里的每一个 path()。匹配过程不是简单的字符串相等,而是基于 path() 函数内部的正则引擎(path() 使用的是更简洁的路径转换器,但底层仍是正则)。它会检查:
- admin/?不匹配,因为路径是 calculate/。
- 空字符串 ''?不匹配,因为路径是 calculate/。
- calculate/?完全匹配!于是,Django 就“决定”将这次请求,交给 views.calculate_view 这个函数来处理。
提示:
name='calculate'这个参数极其重要。它为这个 URL 模式起了一个“别名”。在 HTML 模板里,你永远不应该硬编码<a href="/calculate/">,而应该写{% url 'calculate' %}。这样做的好处是,万一将来你把路径改成path('compute/', ...),所有模板里的链接会自动更新,无需全局搜索替换。这是 Django “解耦”思想的典型体现。
新手常犯的一个错误是,在 path() 中写错应用名。比如,如果你的 views.py 文件不在 blog/ 目录下,而是在项目根目录,那么 from blog import views 就会报 ModuleNotFoundError。正确的做法是:确保 blog/ 是一个合法的 Django 应用(即目录下有 apps.py 和 __init__.py),并且在 settings.py 的 INSTALLED_APPS 里注册了 'blog'。你可以用 python manage.py show_urls(需安装 django-extensions)命令,实时查看当前所有已注册的 URL 映射,这是排查路由问题的终极利器。
3.2 视图函数 (views.py):数据是如何从 Python 世界“渡”到 HTML 世界的?
视图函数是整个服务端渲染流程的“心脏”。它接收一个 HttpRequest 对象(代表浏览器发来的请求),并必须返回一个 HttpResponse 对象(代表要发回给浏览器的响应)。Django 提供了 render() 这个便捷函数,它内部完成了 HttpResponse 的构造和 HTML 渲染。我们来看 calculate_view 的完整实现:
# views.py
from django.shortcuts import render
from django.http import HttpResponse
from . import calculate # 导入同目录下的 calculate.py 模块
def calculate_view(request):
# 1. 初始化默认上下文
context = {
'result': None,
'error': None,
'steps': []
}
# 2. 检查请求方法
if request.method == 'POST':
try:
# 3. 从 POST 数据中提取参数
a = float(request.POST.get('a', '0'))
b = float(request.POST.get('b', '0'))
operation = request.POST.get('operation', 'add')
# 4. 调用计算模块
if operation == 'add':
result = calculate.add(a, b)
steps = [f"{a} + {b} = {result}"]
elif operation == 'multiply':
result = calculate.multiply(a, b)
steps = [f"{a} × {b} = {result}"]
else:
raise ValueError("Unsupported operation")
# 5. 更新上下文
context.update({
'result': result,
'steps': steps,
'a': a,
'b': b,
'operation': operation
})
except (ValueError, TypeError) as e:
context['error'] = f"计算错误:{str(e)}"
# 6. 渲染模板并返回响应
return render(request, 'result.html', context)
这段代码的精妙之处在于它的“防御性编程”设计。它没有假设用户一定会输入数字,而是用 try...except 包裹所有可能出错的环节。request.POST.get('a', '0') 这行代码,get() 方法的第二个参数 '0' 是默认值,意味着如果表单里根本没有 a 这个字段,就用字符串 '0' 代替,避免 KeyError。紧接着的 float() 转换,如果用户输入了 "abc",就会触发 ValueError,被 except 捕获,并把友好的错误信息放进 context['error'],最终在 result.html 里用 {% if error %}<div class="error">{{ error }}</div>{% endif %} 显示出来。
注意:
render()函数的第三个参数context必须是一个字典(dict)。Django 模板引擎会把这个字典的 key 当作变量名,value 当作变量值。所以你在模板里写{{ result }},Django 就会去context字典里找 key 为'result'的值。如果context里没有这个 key,模板引擎会静默地渲染为空字符串,而不会报错——这是一个非常重要的特性,也是新手调试时容易忽略的点:如果你发现{{ result }}没显示,第一反应不应该是“模板写错了”,而应该是“views.py里有没有把result放进context字典?”
3.3 计算模块 (calculate.py):业务逻辑为何要“隔离”?
calculate.py 是这个项目里最“干净”的文件,它不依赖 Django 的任何东西,就是一个纯粹的 Python 模块:
# calculate.py
def add(a: float, b: float) -> float:
"""执行加法运算"""
return a + b
def multiply(a: float, b: float) -> float:
"""执行乘法运算"""
return a * b
def is_positive(number: float) -> bool:
"""判断数字是否为正数"""
return number > 0
将业务逻辑抽离到独立模块,有三大不可替代的好处:
-
可测试性(Testability):你可以完全脱离 Django 环境,用标准的
unittest或pytest来测试这些函数。例如:
```python
# test_calculate.py
import unittest
from calculate import add, multiplyclass TestCalculate(unittest.TestCase):
def test_add(self):
self.assertEqual(add(2, 3), 5)
self.assertEqual(add(-1, 1), 0)def test_multiply(self): self.assertEqual(multiply(4, 5), 20)`` 运行python -m unittest test_calculate.py`,就能快速验证核心业务逻辑的正确性,而无需启动整个 Django 服务。 -
可替换性(Replaceability):假如某天,老板说“我们要把本地计算换成调用阿里云的数学 API”,你只需要重写
calculate.py里的函数,把return a + b换成requests.post('https://api.aliyun.com/math', json={'a': a, 'b': b}),views.py和templates/一行代码都不用改。这就是“关注点分离”的威力。 -
可读性(Readability):当
views.py里充斥着a + b、a * b这样的表达式时,业务意图是模糊的。而calculate.add(a, b)这一行,清晰地表达了“我要执行一个加法操作”,语义明确,团队协作时一目了然。
3.4 HTML 模板 (hello.html 和 result.html):Django 模板引擎的“填空游戏”
Django 模板不是 HTML,而是“带有指令的 HTML”。它有两大核心语法:变量(Variables) 和 标签(Tags)。hello.html 是一个极简的表单页面:
<!-- hello.html -->
<!DOCTYPE html>
<html>
<head>
<title>Django 计算器</title>
</head>
<body>
<h1>欢迎使用 Django 计算器</h1>
<form action="{% url 'calculate' %}" method="post">
{% csrf_token %}
<label for="a">数字 A:</label>
<input type="number" id="a" name="a" step="any" required><br><br>
<label for="b">数字 B:</label>
<input type="number" id="b" name="b" step="any" required><br><br>
<label for="operation">运算类型:</label>
<select id="operation" name="operation">
<option value="add">加法 (+)</option>
<option value="multiply">乘法 (×)</option>
</select><br><br>
<button type="submit">计算</button>
</form>
</body>
</html>
这里有几个新手必知的关键点:
{% url 'calculate' %}:这是前面提到的 URL 别名的使用。它会动态生成/calculate/这个路径,确保表单提交到正确的地址。{% csrf_token %}:这是 Django 的跨站请求伪造(CSRF)保护机制。任何 POST 表单都必须包含这个标签,否则 Django 会直接拒绝请求,并返回 403 Forbidden 错误。这是安全底线,绝不能省略。step="any":这个 HTML 属性允许<input type="number">接受小数,否则默认只能输入整数。
result.html 则展示了模板引擎最强大的能力——动态渲染:
<!-- result.html -->
<!DOCTYPE html>
<html>
<head>
<title>计算结果</title>
</head>
<body>
<h1>计算结果</h1>
<!-- 显示错误信息 -->
{% if error %}
<div style="color: red;">{{ error }}</div>
{% endif %}
<!-- 显示成功结果 -->
{% if result is not None %}
<p><strong>您输入的数字:</strong>A = {{ a }}, B = {{ b }}, 运算 = {{ operation }}</p>
<p><strong>计算结果:</strong>{{ result }}</p>
<h2>计算步骤:</h2>
<ul>
{% for step in steps %}
<li>{{ step }}</li>
{% endfor %}
</ul>
<!-- 条件渲染:如果结果是正数,显示鼓励信息 -->
{% if result|add:"0" > 0 %}
<p style="color: green;">🎉 恭喜!结果是正数!</p>
{% endif %}
{% endif %}
<!-- 返回首页的链接 -->
<p><a href="{% url 'home' %}">← 返回首页</a></p>
</body>
</html>
这段模板里,{% if %}、{% for %}、{% endif %}、{% endfor %} 是模板标签,它们控制 HTML 的结构和逻辑流;{{ error }}、{{ result }}、{{ a }} 是模板变量,它们是 views.py 传递过来的数据。特别注意 {% if result|add:"0" > 0 %} 这一行:|add:"0" 是一个过滤器(Filter),它的作用是把 result 变量强制转换为数字(因为模板里所有变量默认都是字符串),这样才能进行数值比较 > 0。如果不加这个过滤器,"5" > "0" 在字符串比较中是 True,但 "10" > "0" 也是 True,而 "2" > "10" 却是 True(因为字符串比较是按 ASCII 码,‘2’ > ‘1’),这会导致逻辑错误。这个细节,是无数新手在模板里做数值判断时踩过的深坑。
4. 实操过程与核心环节实现:从零开始搭建并运行这个项目
4.1 环境准备:三步走,告别“环境地狱”
很多新手卡在第一步:环境配不起来。这个项目的设计,就是为了让你绕过所有环境陷阱。以下是经过千百次验证的、最稳妥的三步法:
第一步:确认 Python 版本
在终端(macOS/Linux)或命令提示符(Windows)中运行:
python --version
# 或者
python3 --version
确保输出是 Python 3.8 或更高版本(推荐 3.9 或 3.10)。Django 4.x 不支持 Python 3.7 及以下版本。
第二步:创建并激活虚拟环境
虚拟环境(venv)是 Python 项目的“保险箱”,它把本项目的依赖和你电脑上其他项目的依赖完全隔离开,避免版本冲突。
# 进入项目根目录(包含 manage.py 的地方)
cd /path/to/your/project
# 创建名为 venv 的虚拟环境(Linux/macOS)
python -m venv venv
# 或者(Windows)
py -m venv venv
# 激活虚拟环境
# Linux/macOS:
source venv/bin/activate
# Windows:
venv\Scripts\activate.bat
激活成功后,你的命令行提示符前会多出 (venv) 字样,这意味着你现在所有的 pip install 操作,都只会安装到这个 venv 文件夹里,不会污染全局 Python。
第三步:安装依赖并迁移数据库
# 在已激活的虚拟环境中,安装 Django
pip install django
# (可选)安装 django-extensions,用于调试
pip install django-extensions
# 运行数据库迁移(初始化 SQLite 数据库)
python manage.py migrate
# 创建一个超级管理员账号(用于访问 /admin/)
python manage.py createsuperuser
# 按照提示输入用户名、邮箱、密码
提示:
python manage.py migrate这条命令,会读取migrations/目录下的.py文件(如0001_initial.py),这些文件是 Django 根据models.py自动生成的“数据库变更脚本”。即使models.py是空的,Django 也会为你创建一些内置的表(如auth_user,django_admin_log),这是 Admin 后台正常工作的基础。所以,migrate是每次部署新环境时的必做步骤,绝不能跳过。
4.2 启动开发服务器与首次访问
一切就绪后,启动 Django 自带的开发服务器:
python manage.py runserver
你会看到类似这样的输出:
Watching for file changes with StatReloader
Performing system checks...
System check identified no issues (0 silenced).
April 05, 2024 - 10:23:45
Django version 4.2.7, using settings 'YlStlHXlVGKckUsdfZ74-master-fb7fafc9bc04a41cf60fe44ef5c03c5d72473bf0.settings'
Starting development server at http://127.0.0.1:8000/
Quit the server with CONTROL-C.
现在,打开你的浏览器,访问 http://127.0.0.1:8000/。你应该能看到 hello.html 渲染出的“欢迎使用 Django 计算器”页面。恭喜,你的第一个 Django 服务端渲染应用,已经成功跑起来了!
4.3 关键配置文件详解:settings.py 里的“开关”
settings.py 是 Django 项目的“大脑”,它控制着一切行为。对于新手,只需要关注以下几个核心配置项:
# settings.py
DEBUG = True # 开发模式开关。设为 True 时,出错会显示详细的调试页面;上线必须设为 False!
ALLOWED_HOSTS = ['127.0.0.1', 'localhost'] # 允许访问的域名列表。开发时填本地地址;上线时必须填你的域名,如 ['myapp.com']
# 模板配置:告诉 Django 到哪里去找 .html 文件
TEMPLATES = [
{
'BACKEND': 'django.template.backends.django.DjangoTemplates',
'DIRS': [BASE_DIR / 'templates'], # 关键!这里指定了 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',
],
},
},
]
# 静态文件配置(本项目未使用,但必须存在)
STATIC_URL = '/static/'
# STATICFILES_DIRS = [BASE_DIR / 'static'] # 如果你以后要加 CSS/JS,就取消这行注释
其中,'DIRS': [BASE_DIR / 'templates'] 这一行是重中之重。它告诉 Django:“请去项目根目录下的 templates 文件夹里,寻找所有 .html 文件。” 如果你把 hello.html 错误地放在了 blog/templates/ 下,而没有在 DIRS 里添加 BASE_DIR / 'blog/templates',那么 Django 就会报错 TemplateDoesNotExist。这是新手最常见的模板找不到错误,根源几乎都在这里。
4.4 项目结构实战演练:动手修改,加深理解
光看是学不会的,必须动手改。下面是一个循序渐进的实战练习,每一步都对应一个核心概念:
练习一:修改首页标题
- 打开 templates/hello.html
- 找到 <h1>欢迎使用 Django 计算器</h1>
- 把它改成 <h1>我的第一个 Django 应用</h1>
- 保存文件,刷新浏览器 http://127.0.0.1:8000/,观察变化。体会:HTML 模板的修改是即时生效的,无需重启服务器。
练习二:增加一个“减法”选项
- 打开 calculate.py,添加一个新函数:
python def subtract(a: float, b: float) -> float: return a - b
- 打开 views.py,在 calculate_view 函数的 if operation == 'add': 分支后面,添加:
python elif operation == 'subtract': result = calculate.subtract(a, b) steps = [f"{a} - {b} = {result}"]
- 打开 templates/hello.html,在 <select> 标签里,添加一个新的 <option>:
html <option value="subtract">减法 (-)</option>
- 保存所有文件,刷新首页,选择“减法”并计算。体会:业务逻辑、视图、模板三者的联动关系。
练习三:让结果页面显示“计算时间”
- 打开 views.py,在 calculate_view 函数的 if request.method == 'POST': 块内,context.update({ 之前,添加:
python import datetime calculation_time = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")
- 然后在 context.update({ 里加入 'calculation_time': calculation_time,
- 打开 templates/result.html,在 <p><strong>计算结果:</strong>{{ result }}</p> 后面,添加:
html <p><strong>计算时间:</strong>{{ calculation_time }}</p>
- 保存,刷新测试。体会:Python 的标准库如何无缝注入到模板中。
5. 常见问题与排查技巧实录:那些年,我们踩过的“坑”
5.1 经典报错速查表
| 报错信息 | 最可能的原因 | 排查与解决步骤 |
|---|---|---|
ModuleNotFoundError: No module named 'blog' | INSTALLED_APPS 里没注册 blog,或 blog/ 目录下缺少 apps.py 或 __init__.py | 1. 检查 settings.py 的 INSTALLED_APPS 是否包含 'blog'。2. 检查 blog/ 目录是否存在,且包含 __init__.py(可以是空文件)。 |
TemplateDoesNotExist at / | Django 找不到 hello.html | 1. 检查 settings.py 的 TEMPLATES 配置中 'DIRS' 是否指向了正确的 templates 路径。2. 检查 templates/ 目录是否在项目根目录下,且 hello.html 文件名拼写正确(大小写敏感!)。 |
Reverse for 'calculate' not found. | urls.py 里没有定义 name='calculate' 的路径,或 name 拼写错误 | 1. 检查 urls.py 中 path(..., name='calculate') 的 name 参数。2. 检查模板里 {% url 'calculate' %} 的字符串是否与 name 完全一致。 |
CSRF verification failed. Request aborted. | 表单里遗漏了 {% csrf_token %} 标签 | 1. 打开 hello.html,确认 <form> 标签内部,<input> 标签之前,有 {% csrf_token %}。2. 确保 settings.py 中 MIDDLEWARE 列表里包含 'django.middleware.csrf.CsrfViewMiddleware'(默认就有)。 |
OperationalError at /calculate/ no such table: blog_calculationhistory | 试图访问一个数据库表,但该表尚未通过 migrate 创建 | 1. 运行 python manage.py showmigrations,查看哪些迁移未应用。2. 运行 python manage.py migrate 执行所有未应用的迁移。 |
5.2 实操心得:那些文档里不会写的“潜规则”
心得一:“manage.py 是你的瑞士军刀,不是摆设”
新手往往只记得 runserver,却忽略了 manage.py 的其他强大功能。以下三个命令,能帮你省下 80% 的调试时间:
- python manage.py show_urls:列出所有已注册的 URL 及其对应的视图函数。当你不确定某个 URL 到底映射到哪个函数时,这是最快的验证方式。
- python manage.py shell:进入 Django 的交互式 Python shell。在这里,你可以直接导入 views、calculate,手动调用函数,测试逻辑,就像在 Jupyter Notebook 里一样。>>> from blog import calculate; print(calculate.add(1, 2))。
- python manage.py dbshell:直接进入 SQLite 数据库命令行。你可以用 SELECT * FROM auth_user; 查看管理员账号是否创建成功,用 .tables 查看所有表名。这是验证数据库状态的最直接手段。
心得二:“DEBUG=True 是你的朋友,但不是永远的朋友”
开发时,DEBUG=True 能让你看到漂亮的、带源码高亮的错误页面,上面有详细的 Traceback 和变量值。但一旦项目要上线,必须将 DEBUG=False。否则,攻击者可以通过这个页面,看到你的 settings.py 里的数据库密码、密钥等敏感信息。而且,DEBUG=False 时,Django 不会自动提供静态文件服务,你需要额外配置 STATIC_ROOT 和 collectstatic 命令,这是生产环境部署的必经之路。
心得三:“__pycache__ 和 .idea 不是垃圾,是你的‘历史记录’”
.idea 是 PyCharm 的项目配置文件夹,__pycache__ 是 Python 编译 .py 文件后生成的字节码缓存。它们不会影响程序运行,但删除它们也不会造成任何损失。.gitignore 文件里已经把它们列进去了,这意味着你 git push 的时候,它们不会上传到 GitHub。所以,放心地让它们存在吧,它们是你开发过程的“无害副产品”。
心得四:“不要迷信‘一键部署’,先学会‘手动部署’”
网上有很多 Docker Compose 一键部署 Django 的教程。但对于新手,我强烈建议你先跳过 Docker,用最原始的方式部署到一台 VPS 上:安装 Python、创建 venv、pip install django、cp 项目文件、python manage.py collectstatic、用 gunicorn 启动服务、用 nginx 做反向代理。这个过程虽然繁琐,但它会让你彻底理解 Django 应用在服务器上究竟是如何“呼吸”的。当你亲手把 gunicorn.sock 文件的权限从 777 改成 644,并因此解决了 502 Bad Gateway 错误时,那种豁然开朗的感觉,是任何自动化脚本都无法给予的。
6. 项目扩展与进阶思考:从“能跑”到“能用”的跨越
这个项目只是一个坚实的起点,而不是终点。当你能熟练地修改 calculate.py、views.py 和 templates/ 并让它们协同工作后,下一步就是给它“添砖加瓦”,让它真正具备实用价值。以下是几个平滑、自然的进阶方向,每一个都建立在你已掌握的核心概念之上:
6.1 引入数据库:从内存计算到持久化存储
目前,所有的计算都是“一闪而过”的。如果用户想知道“我昨天算过什么?”,现在的项目无法回答。这时,models.py 就不再是占位符了。你可以这样扩展:
# models.py
from django.db import models
class CalculationHistory(models.Model):
a = models.FloatField()
b = models.FloatField()
operation = models.CharField(max_length=20)
result = models.FloatField()
created_at = models.DateTimeField(auto_now_add=True)
def __str__(self):
return f"{self.a} {self.operation} {self.b} = {self.result}"
然后,在 views.py 的 calculate_view 函数里,context.update({ 之后,添加:
# 保存到数据库
CalculationHistory.objects.create(
a=a,
b=b,
operation=operation,
result=result
)
最后,运行 python manage.py makemigrations 和 python manage.py migrate,Django 就会为你创建 blog_calculationhistory 这张表。你会发现,migrations/ 目录下多了一个新的 .py 文件,这就是 Django 为你生成的“数据库变更说明书”。这个过程,完美地衔接了你已有的 migrate 知识,只是从“初始化内置表”升级到了“创建自定义表”。
6.2 添加用户认证:从“匿名计算”到“个人空间”
admin.py 文件目前是空的,但它是一个金矿。你可以把它变成一个强大的后台管理界面:
# admin.py
from django.contrib import admin
from .models import CalculationHistory
@admin.register(CalculationHistory)
class CalculationHistoryAdmin(admin.ModelAdmin):
list_display = ('a', 'b', 'operation', 'result', 'created_at')
list_filter = ('operation', 'created_at')
search_fields = ('a', 'b', 'result')
然后,用你之前创建的超级管理员账号,登录 http://127.0.0.1:8000/admin/,就能看到一个功能完备的、可以增删改查所有计算历史的后台。这不需要你写一行前端代码,Django 已经为你封装好了。这就是框架的力量——它把重复的、模式化的 CRUD(创建、读取、更新、删除)工作,变成了几行 Python 代码。
6.3 集成静态资源:从“裸 HTML”到“有样式的页面”
当你的 templates/ 里开始出现大量重复的 <style> 标签时,就是该引入 static/ 目录的时候了。创建 static/css/main.css,把所有 CSS 移进去;在 settings.py 里取消 STATICFILES_DIRS 的注释;然后在 hello.html 的 <head> 里添加:
{% load static %}
<link rel="stylesheet" type="text/css" href="{% static 'css/main.css' %}">
{% load static %} 是一个模板标签,它“加载”了 Django 的静态文件处理功能;{% static 'css/main.css' %} 则会根据 STATIC_URL 配置,动态生成正确的 URL,比如 /static/css/main.css。这个过程,再次印证了 Django 的核心哲学:一切皆可配置,一切皆可抽象,但抽象的目的是为了让你更专注于业务本身,而不是底层细节。
最后再分享一个小技巧:这个项目的所有文件,包括 manage.py、settings.py、urls.py,它们的名字和位置,都不是随意定的。Django 的启动流程,是严格按照一个预设的“约定优于配置”(Convention over Configuration)路径来查找的。manage.py 是入口,它会去 settings.py 里找配置,settings.py 会去 urls.py 里找路由,urls.py 会去 views.py 里找函数…… 这套约定,是 Django 社区十年沉淀下来的最佳实践。当你有一天,看到一个陌生的 Django 项目,不需要阅读任何文档,只要扫一眼它的目录结构,就能大致猜出它的数据流向和核心逻辑——那一刻,你就真正读懂了 Django。
简介:一个开箱即用的Django入门项目,专注传统服务端渲染流程,不引入任何前端框架。通过简单的URL配置(urls.py)将请求分发到视图函数(views.py),视图调用calculate.py完成基础业务计算,并把结果连同上下文数据传入hello.html和.html等HTML模板,由Django内置模板引擎完成最终渲染。项目结构完整,包含标准Django应用组件:models.py定义数据模型,admin.py支持后台管理,apps.py声明应用配置,migrations目录管理数据库迁移,templates文件夹集中存放所有HTML模板,settings.py提供调试与部署相关设置,manage.py用于常规命令操作,wsgi.py和asgi.py分别适配WSGI/ASGI服务器部署。配套SQLite数据库(db.sqlite3)已初始化,虚拟环境(venv)和开发配置(.gitignore、.idea、pycache)均已就位,不影响运行。适合新手动手实践请求生命周期、URL映射、视图响应、模板变量与标签、静态资源组织等核心Web开发环节。
340



被折叠的 条评论
为什么被折叠?



