1. Django 编写视图的方式
1.1 三种方式
- 在 Django 中有三种不同的方式来编写视图:
- 基于函数的
- 基于类
通用的基于类的
- 通用的基于类的视图是最难理解的。整整一个网站《Classy Class-Based Views》专门用于帮助开发者解析它们。
1.2 三种方式如何选择
- 视图的三种不同编写方式的推荐方法:
- 首先会选择
通用的基于类的视图。有很多问题可以通过现成的 GCVB(如 TemplateView、ListView 或 DetailView)解决。 - 当通用基于类的视图不足以满足需求时,可以对其进行
修改以适应你的需求。 - 如果这仍然不够,那么就回退到
基于函数或基于类的视图。
- 首先会选择
2. 创建项目
2.0 前置操作
-
如上一篇博客《【Web应用开发笔记】Django笔记2:一个 Hello World 网页》中介绍了创建项目相关的操作,这里不再赘述。总结如下:
- 创建 python 环境(包括导入包)
- 创建 Django 项目
- 创建 pages APP(包括修改相关代码)
-
后续的操作均
基于上一篇博客创建的项目。 -
这里我补充一点:
- 我将之前名为 py3p11_Django 的 python 环境删除了。
- 与 conda 方式安装 python 环境不同的是,通过 apt install 加 python3.11 -m venv 方式创建的环境只需要直接 rm -rf 环境文件夹即可删除环境。
- 我按照书里说的重新创建了名为 .venv 的环境,主要是为了让这个目录在项目里看起来更规范。
-
在创建以及修改项目的过程中,记住下面的图有助于你理清思路

2.1 第一个 Django 模版:Home 页面
2.1.1 创建模版页面
- 在项目中创建一个 templates 文件夹
bash
mkdir templates
现在的项目目录结构是这样
text
├── .venv/
├── django_project/
├── pages/
├── templates/ # new <===
├── db.sqlite3
└── manage.py
- 在 django_project/settings.py 中修改 TEMPLATES 变量如下
python
TEMPLATES = [
{
"BACKEND": "django.template.backends.django.DjangoTemplates",
"DIRS": [
BASE_DIR / "templates" # new <===
],
"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",
],
},
},
]
- 在 templates/ 文件夹下创建 home.html
html
<!-- templates/home.html -->
<h1>Homepage</h1>
2.1.2 修改 pages APP 相关代码
- pages/views.py
python
from django.views.generic import TemplateView
class HomePageView(TemplateView):
template_name = "home.html"
- pages/urls.py
python
from django.urls import path
from .views import HomePageView
urlpatterns = [
path("", HomePageView.as_view(), name="home"),
]
- django_project/urls.py
python
from django.contrib import admin
from django.urls import path, include # new
urlpatterns = [
path("admin/", admin.site.urls),
path("", include("pages.urls")), # new
]
2.1.3 重启服务
bash
python manage.py runserver

2.2 第二个 Django 模版:About 页面
2.2.1 新页面需要修改哪些东西
由于我们前面已经创建过一个页面,并且修改了一些代码,因此在新增一个独立页面的时候,就不需要修改那么多地方了。
我们需要修改的是
- templates/ 下新增 About页面
- 修改 pages APP 下的 views 和 urls
2.2.2 新增、修改的具体内容
- 新增 templates/about.html
python
<!-- templates/about.html -->
<h1>About page</h1>
- 修改 pages/views.py
python
from django.views.generic import TemplateView
class HomePageView(TemplateView):
template_name = "home.html"
class AboutPageView(TemplateView): # new <===
template_name = "about.html"
- 修改 pages/urls.py
python
from django.urls import path
from .views import HomePageView, AboutPageView # new <===
urlpatterns = [
path("", HomePageView.as_view(), name="home"),
path("about/", AboutPageView.as_view(), name="about"), # new <===
]
2.2.3 重启服务
注意这里的网址需要加 about/ 结尾,因为我们在 pages/urls.py 里面给 About页面 配置的路径就是这个

2.3 扩展模版
在上面的例子里,有一些问题:
- 这两个页面还没有联系,比如我在 home 页面是无法跳转到 about 页面的。
- 如果这两个页面需要相同的某些内容,比如一个公共的头部导航之类的,可以有什么方式来复用吗?
本小节就来解决以上问题。
2.3.1 页面修改
- 新建 templates/base.html
html
<!-- templates/base.html -->
<header>
<a href="{% url 'home' %}">Home</a> |
<a href="{% url 'about' %}">About</a>
</header>
{% block content %} {% endblock content %}
- 修改 templates/home.html
html
<!-- templates/home.html -->
{% extends "base.html" %}
{% block content %}
<h1>Homepage</h1>
{% endblock content %}
- 修改 templates/about.html
html
<!-- templates/about.html -->
{% extends "base.html" %}
{% block content %}
<h1>About page</h1>
{% endblock content %}
2.3.2 重启服务

可以看到:
- 这个页面上有了简单的导航,通过点击 Home | About 就可以跳转到对应的页面上。
- Home 和 About 页面上都有第一行这个导航。
2.3.3 ★ 解析 ★
作为新手,这三个 html 虽然内容不多,但是确实需要花费一定精力来理解。
前面的三个 html 实际是 html 语法 和 Django 模版语法的混合写法,是 Django 为了模版专门适配的写法。
下面简单介绍。
2.3.3.1 html 语法
- 写法
html
<!-- 注释 -->
<标签名 属性="值">内容</标签名>
- 举例
html
<!-- 示例 -->
<a href="https://example.com">点击这里</a>
- 标签可嵌套
2.3.3.2 Django 模版在 html 中的语法
| 语法 | 类型 | 含义 |
|---|---|---|
{% extends "base.html" %} |
模板标签 | 继承 父模板,必须放在子模板最顶部 |
{% block content %} |
模板标签 | 定义一个名为 "content" 的内容块(开始) |
{% endblock content %} |
模板标签 | 结束 "content" 块(content 可省略,但建议写上) |
{% url 'home' %} |
模板标签 | 根据 URL 名称反向解析出实际 URL ,这和 urls.py 代码中指定的 name="home"是对应的 |
{``{ variable }} |
模板变量 | 输出变量值(双花括号,你例子中没用到) |
- 关于 {% block content %} {% endblock content %}
- 父模板(base.html)用 {% block content %} 挖一个"坑"
- 子模板(home.html/about.html)用同样的 {% block content %} 来"填坑"
- 块名(content)可以自定义,但父子必须匹配
- 由此再回顾三个 html 的内容,可以看出它们之间的关系:
text
base.html (父模板/基础框架)
├── home.html (子模板) ← 填充 content 块为 "Homepage"
└── about.html (子模板) ← 填充 content 块为 "About page"
2.3.3.4 总结
| 概念 | 属于 | 作用 |
|---|---|---|
<a>、<header>、<h1> |
HTML | 定义网页结构和语义 |
{% extends %}、{% block %}、{% url %} 等 |
Django 模板语言 | 实现模板继承和动态内容 |
name='home' |
Django URL 配置 py代码 | 为 URL 路径提供可引用的标识符 |
3. 测试
3.1 简介
- 测试的目的
- 保证功能
- 保障性能
- 保障稳定性
- 测试的类型
- 单元测试:模块
- 集成测试:整体
- 注意点
- 例如新增了功能,此时不仅需要验证新功能是否正常(单元测试),也要保证新功能不会影响老功能(集成测试)
3.2 python & Django 提供的标准测试工具
- python
- 内置的测试框架:unittest
- 使用 TestCase 实例
- 长的断言方法
- 内置的测试框架:unittest
- Django
- 用于发起模拟 Web 浏览器请求的测试客户端
- Django 特有的额外断言
- 四个测试用例类:
- SimpleTestCase:不需要数据库时使用
- TestCase:想测试数据库时使用
- TransactionTestCase:对直接测试数据库事务很有帮助
- LiveServerTestCase:启动一个实时服务器线程,用于与基于浏览器的工具(如 Selenium)进行测试
| 测试类 | 用途 | 数据库支持 |
|---|---|---|
SimpleTestCase |
测试不依赖数据库的基本功能(URL、视图、模板) | ❌ 不创建测试数据库 |
TestCase |
测试依赖数据库的功能(模型、表单、数据库操作) | ✅ 自动创建并隔离测试数据库 |
LiveServerTestCase |
启动真实服务器进行集成测试(如 Selenium 测试) | ✅ 支持 |
TransactionTestCase |
测试数据库事务行为 | ✅ 每个测试后回滚事务 |
-
基本用法
继承4个基础类中的某一个- 写
测试函数(类的成员函数)。Django 基于 Python 的 unittest 框架,只要求遵循一个约定:- 函数名必须
以 test_ 开头
- 函数名必须
- 运行测试
python manage.py test
-
常用工具
- self.client:内置的 HTTP 客户端,模拟 GET/POST 请求
- reverse():根据 URL 名称反向解析路径(与模板中的 {% url %} 对应)
- 断言方法:assertEqual()、assertContains()、assertTemplateUsed() 等
-
注:unittest 和 django.test 中方法的命名是使用 camelCase 而不是更符合 Python 风格的 snake_case。原因是 unittest 基于 Java 的 jUnit 测试框架,而 jUnit 使用的是 camelCase,因此当 Python 引入 unittest 时,也采用了 camelCase 命名。
3.3 测试本项目
| 测试内容 | 成功标志 |
|---|---|
| url是否能够访问 | 是否都返回 HTTP 状态码 200,这是 HTTP 请求成功的标准响应 |
| 页面展示是否正常 | 可以测试的内容包括:页面名称是否正确、模版html是否正常使用、页面返回内容是否包含目标内容 |
- 测试内容:
- home 和 about 页面的url是否能够访问 - 是否都返回 HTTP 状态码 200,这是 HTTP 请求成功的标准响应
- 关键:是否都返回 HTTP 状态码 200,这是 HTTP 请求成功的标准响应
- 代码
python
# pages/tests.py
from django.test import SimpleTestCase
from django.urls import reverse
class HomepageTests(SimpleTestCase):
def test_url_exists_at_correct_location(self):
response = self.client.get("/")
self.assertEqual(response.status_code, 200)
def test_url_available_by_name(self):
response = self.client.get(reverse("home"))
self.assertEqual(response.status_code, 200)
def test_template_name_correct(self):
response = self.client.get(reverse("home"))
self.assertTemplateUsed(response, "home.html")
def test_template_content(self):
response = self.client.get(reverse("home"))
self.assertContains(response, "<h1>Homepage</h1>")
class AboutpageTests(SimpleTestCase):
def test_url_exists_at_correct_location(self):
response = self.client.get("/about/")
self.assertEqual(response.status_code, 200)
def test_url_available_by_name(self):
response = self.client.get(reverse("about"))
self.assertEqual(response.status_code, 200)
def test_template_name_correct(self):
response = self.client.get(reverse("about"))
self.assertTemplateUsed(response, "about.html")
def test_template_content(self):
response = self.client.get(reverse("about"))
self.assertContains(response, "<h1>About page</h1>")
- 解释(以 HomepageTests 中的四个用例为例, about 页面同理不赘述)
| 测试方法 | 访问方式 | 测试重点 |
|---|---|---|
test_url_exists_at_correct_location |
"/" 硬编码路径 |
验证根路径是否存在且可访问(HTTP 200) |
test_url_available_by_name |
reverse("home") 反向解析 |
验证 URL 名称 home 能正确解析到对应路径 |
test_template_name_correct |
reverse("home") 反向解析 |
验证视图渲染的是正确的模板文件 home.html |
test_template_content |
reverse("home") 反向解析 |
验证返回的页面内容中包含预期的 HTML 片段 <h1>Homepage</h1> |
text
test_url_exists_at_correct_location → 路径通不通?(最基础)
↓
test_url_available_by_name → 名称解析对不对?
↓
test_template_name_correct → 用了哪个模板?
↓
test_template_content → 内容对不对?(最严格)
- 运行
bash
python manage.py test

这表示 home 页面和 about 页面的8个用例均测试通过
4. 小结:创建一个不涉及数据库的 Web 应用
本文重点用 ☆ 标记
- 环境配置:python、Django
- 创建项目
- 创建初始数据库 并 注册到 settings 中 (仅为了消除告警)
- 创建 APP 并 注册到 settings 中
- 创建模版 html ☆
- 创建 view(指向 html) ☆
- 创建 URL 配置 (使用 view) ☆
- 测试 ☆
- 编写测试用例 ☆
- 测试 ☆
- 启动服务
- 人工检视