外观
Django框架-构建博客应用程序
约 4778 字大约 16 分钟
2026-03-24
本节带大家使用 Django 构建专业级 Web 项目。一个功能齐全的 Web 应用程序主要组件有:模型、模板、视图和 URL。通过本节训练,大家可以了解 Django 的工作原理以及各个框架组件之间的交互方式。
本章主要内容:
搭建 Django 开发环境
创建和配置 Django 项目
创建 Django 应用
设计数据模型
创建和执行数据库迁移
为模型配置后台管理
使用 QuerySet 和模型管理器查询数据
编写视图、模板和 URL 配置
理解 Django 请求/响应周期
一、 构建博客前准备
注:准备Python3.12版本的虚拟环境,并安装Django5.2版本框架。若已完成请跳过。
1. 使用Python3.12的版本:
Django 5.2 支持 Python 3.10、3.11、3.12 和 3.13。接下来我们使用 Python 3.12。
如果你的 Python 版本低于 3.12,或者还没有安装 Python,请到https://www.python.org/downloads/下载 Python 3.12 并安装。
python3 --version
python -V
# Python 3.12.92. 创建Python虚拟环境:
- 从 Python 3.3 开始,Python 自带了
venv模块,可以用来创建轻量级的虚拟环境。
# 1. 创建虚拟环境
python -m venv my_env
# 2. 激活虚拟环境
# Linux下的激活虚拟环境
#source my_env/bin/activate
# Windows下的激活虚拟环境
.\my_env\Scripts\activate
#3. Linux下的验证激活
# (my_env) $ which python
# Windows下的验证激活
(my_env) PS> Get-Command python
# 4. 停用虚拟环境
(my_env) $ deactivate3. 安装Django
- 作为Python Web框架,Django需要Python,在安装Python同时需要安装pip。
# 在线安装Django,指定版本安装,目前5.2的最新版为5.2.9
python -m pip install django==5.2
# 默认会安装:Django==5.2.9、sqlparse==0.5.4、asgiref==3.11.0 和 tzdata==2025.3
# 可以使用pip list查看
# 检测当前是否安装Django及版本
python -m django --version
5.2.9
# 我们也可以先下载安装包:pip download django=4.2.18 -d ./二、创建项目与构建博客应用
1. 创建项目mysite
在当前Python虚拟环境中运行以下命令创建mysite项目:
$ django-admin startproject mysitestartproject 命令自动创建的项目结构如下:
mysite/ # 外部mysite/根目录只是一个项目的容器
├── manage.py # 命令行实用程序,实现与此Django项目进行各种交互
└── mysite # 内部mysite/目录是当前项目的实际Python包
├── __init__.py # 空文件,代表Python包的标志文件
├── settings.py # 此Django项目的设置/配置 文件
├── urls.py # 该Django项目的URL路由声明
├── asgi.py # ASGI兼容的Web服务器为您的项目提供服务的入口点
└── wsgi.py # WSGI兼容的Web服务器为您的项目提供服务的入口点2. 创建应用程序blog
Django 自带一个实用工具,可以自动生成应用的基本目录结构,这样你就可以专注于写代码,而不用手动建目录。
确保你在 manage.py 所在的目录下,然后运行以下命令来创建应用:
$ cd mysite
$ python manage.py startapp blog- 运行后会生成一个 blog 目录,结构如下:
mysite/
├── manage.py
├─── mysite
│ ├── __init__.py
│ ├── settings.py
│ ├── urls.py
│ ├── asgi.py
│ └── wsgi.py
└── blog # blog/目录为博客应用程序目录名称(就是一个Python包)
├── __init__.py # 空文件,代表Python包的标志文件
├── admin.py # 用于在Django管理站点中注册模型的,使用此站点是可选的
├── apps.py # 包括blog应用程序的主要配置
├── migrations # 此目录将包含应用程序的数据库迁移文件(Python包)
│ └── __init__.py
├── models.py # 应用程序的数据模型,每个应用程序的必须文件(可留空)
├── tests.py # 在这里为您的应用程序添加测试
└── views.py # 应用程序的逻辑写在这里,HTTP请求、处理并返回响应。3. 启用网站Admin管理
(1). 项目配置文件的设置
打开 mysite/settings.py 文件,进行以下配置:
默认情况下使用 SQLite 数据库,不需要额外配置 DATABASES:
# SQLite数据库的连接配置
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.sqlite3',
'NAME': BASE_DIR / 'db.sqlite3',
}
}- 设置时区和语言,让admin后台显示中文版界面:
...
LANGUAGE_CODE = 'zh-hans' # 语言设置
TIME_ZONE = 'Asia/Shanghai' # 时区设置
...(2). 数据迁移
- 运行以下命令,把 Django 自带的管理功能所需的数据表结构迁移到数据库:
$ python manage.py migrate- 也可以执行下面的命令查看迁移状态:
$ python manage.py showmigrations- 具体执行效果如下:
# 执行上面命令后的输出结果
$ python manage.py migrate
Operations to perform:
Apply all migrations: admin, auth, contenttypes, sessions
Running migrations:
Applying contenttypes.0001_initial... OK
Applying auth.0001_initial... OK
Applying admin.0001_initial... OK
Applying admin.0002_logentry_remove_auto_add... OK
Applying contenttypes.0002_remove_content_type_name... OK
Applying auth.0002_alter_permission_name_max_length... OK
Applying auth.0003_alter_user_email_max_length... OK
Applying auth.0004_alter_user_username_opts... OK
Applying auth.0005_alter_user_last_login_null... OK
Applying auth.0006_require_contenttypes_0002... OK
Applying auth.0007_alter_validators_add_error_messages... OK
Applying auth.0008_alter_user_username_max_length... OK
Applying sessions.0001_initial... OK
# 执行下面命令查看迁移状态,具体如下:
$ python manage.py showmigrations
admin
[X] 0001_initial
[X] 0002_logentry_remove_auto_add
[X] 0003_logentry_add_action_flag_choices
auth
[X] 0001_initial
[X] 0002_alter_permission_name_max_length
[X] 0003_alter_user_email_max_length
[X] 0004_alter_user_username_opts
[X] 0005_alter_user_last_login_null
[X] 0006_require_contenttypes_0002
[X] 0007_alter_validators_add_error_messages
[X] 0008_alter_user_username_max_length
[X] 0009_alter_user_last_name_max_length
[X] 0010_alter_group_name_max_length
[X] 0011_update_proxy_permissions
[X] 0012_alter_user_first_name_max_length
contenttypes
[X] 0001_initial
[X] 0002_remove_content_type_name
sessions
[X] 0001_initial(3). 创建管理员用户
首先,我们需要创建一个可以登录后台管理的超级用户。运行以下命令:
$ python manage.py createsuperuser
# 输入用户名,然后按回车:
Username: admin
# 接着输入邮箱地址:
Email address: admin@example.com
# 最后输入密码(不少于8位),需要输入两次确认:
Password: **********
Password (again): *********
Superuser created successfully.(4). 启动开发服务器
Django 后台管理默认就是开启的。启动开发服务器来看看效果:
$ python manage.py runserver
或
$ python manage.py runserver 0.0.0.0:8000现在,打开浏览器,访问地址: http://127.0.0.1:8000/admin/
使用刚刚创建的账号密码进行登录测试:

三、博客应用中的模型
1. 应用中创建模型
在我们的blog应用程序中,去创建一个post表信息操作的Model类。
编辑 blog/models.py文件,完整博文post的model类信息如下:
from django.db import models
from django.utils import timezone
class Post(models.Model):
class Status(models.TextChoices):
DRAFT = 'DF', '草稿'
PUBLISHED = 'PB', '已发布'
title = models.CharField('文章标题',max_length=250)
# SlugField 是 Django 中的一个特殊字符字段,专门用于存储 URL 友好的字符串。
slug = models.SlugField('slug',max_length=250) # 用于博文的URL别名
body = models.TextField('文章内容')
publish = models.DateTimeField('发布时间',default=timezone.now)
created = models.DateTimeField('创建时间',auto_now_add=True)
updated = models.DateTimeField('更新时间',auto_now=True)
status = models.CharField(
'文章状态',
max_length=2,
choices=Status,
default=Status.DRAFT
)
# 自定义对应的表名,默认表名:blog_post
class Meta:
db_table="blog_post"
verbose_name = '博文信息' # 在Admin中显示的单数形式名称
verbose_name_plural = '博文信息管理'# 在Admin中显示的复数形式名称
ordering = ['-publish'] # 默认排序方式,负号表示降序
indexes = [ # 为publish字段创建索引
models.Index(fields=['-publish']),
]
# 返回对象的字符串表示
def __str__(self):
return self.title下面对这个 Model 的各部分进行详细说明:
① 博文基本信息
- 博文基本字段如下:
from django.db import models
class Post(models.Model):
title = models.CharField('文章标题',max_length=250)
slug = models.SlugField('slug',max_length=250) # 用于博文的URL别名
body = models.TextField('文章内容')
# 返回对象的字符串表示
def __str__(self):
return self.titletitle是文章标题字段,类型为CharField,对应数据库中的VARCHAR列。slug类型为SlugField,同样对应数据库中的VARCHAR列。Slug 是一种 URL 友好的短标签,只能包含字母、数字、下划线或连字符。
Title 到 Slug 的打磨
Translating Title to Slug
↓
去除特殊符号⚙
全转换小写↔
空格转连字符 /blog/post/your-slug-here/
Slug 是为了生成美观、对搜索引擎友好的 URL 标识符,而将非规范字符打磨、切削而成的纯净字符串连字符组合。
A slug is an SEO-friendly URL string generated by stripping out special characters, lowercasing, and replacing spaces with hyphens.
body是文章正文字段,类型为TextField,对应数据库中的TEXT列。

② 添加日期时间字段
- 为博文 Post 添加三个日期时间字段:发布时间、创建时间和更新时间。
from django.db import models
from django.utils import timezone
class Post(models.Model):
...
publish = models.DateTimeField('发布时间',default=timezone.now)
created = models.DateTimeField('创建时间',auto_now_add=True)
updated = models.DateTimeField('更新时间',auto_now=True)
...publish字段:存储博文的发布日期和时间。created字段:记录博文创建的日期和时间。设置了auto_now_add=True,创建对象时会自动记录当前时间。updated字段:记录博文最后更新的日期和时间。设置了auto_now=True,每次保存对象时会自动更新为当前时间。
③ 定义默认排序顺序
from django.db import models
from django.utils import timezone
class Post(models.Model):
...
class Meta:
ordering = ['-publish'] # 默认排序方式,负号表示降序
...我们在模型中添加了一个
Meta内部类,用来定义模型的元数据。通过ordering属性,告诉 Django 默认按publish字段排序。字段名前加连字符(
-)表示降序排列,即-publish表示按发布时间从新到旧排序。
④ 添加数据库索引
from django.db import models
from django.utils import timezone
class Post(models.Model):
...
class Meta:
...
indexes = [ # 为publish字段创建索引
models.Index(fields=['-publish']),
]
...在 Meta 类中通过 indexes 选项可以为模型定义数据库索引。索引可以包含一个或多个字段,支持升序或降序。添加索引后,数据库会为对应的表创建索引,从而加快查询速度。
⑤ 添加状态字段
from django.db import models
from django.utils import timezone
class Post(models.Model):
class Status(models.TextChoices):
DRAFT = 'DF', '草稿'
PUBLISHED = 'PB', '已发布'
... ...
status = models.CharField(
'文章状态',
max_length=2,
choices=Status,
default=Status.DRAFT
)
... ...这里通过继承 models.TextChoices 定义了一个 Status 枚举类,包含两个选项:"草稿"和"已发布",对应的存储值分别是 DF 和 PB。
2. 激活应用程序
- 要让 Django 识别我们创建的 blog 应用,需要在 settings.py 的
INSTALLED_APPS中注册。
INSTALLED_APPS = [
'django.contrib.admin' ,
'django.contrib.auth' ,
'django.contrib.contenttypes' ,
'django.contrib.sessions' ,
'django.contrib.messages' ,
'django.contrib.staticfiles' ,
# 自定义blog应用的配置
'blog.apps.BlogConfig', # 使用配置类而不是'app名'
# 或者简写(如果使用自动发现)
# 'blog',
]3. 应用数据库迁移
# 创建迁移文件,在blog/migrations/目录下:
$ python manage.py makemigrations blog
Migrations for 'blog':
blog/migrations/0001_initial.py
+ Create model Stu
# 执行应用迁移
$ python manage.py migrate
Operations to perform:
Apply all migrations: admin, auth, blog, contenttypes, sessions
Running migrations:
Applying blog.0001_initial... OK
# 查看迁移状态
python manage.py showmigrations4. 将模型注册到后台管理
我们自定义的 blog 应用默认不会出现在后台管理页面上,需要手动注册。
打开 blog/admin.py 文件,添加以下代码:
from django.contrib import admin
from .models import Post
admin.site.register(Post)启动服务:
$ python manage.py runserver
或
$ python manage.py runserver 0.0.0.0:8000现在打开浏览器,访问 http://127.0.0.1:8000/admin/
5. 自定义模型显示方式
继续编辑 blog/admin.py 文件,按如下方式修改:
from django.contrib import admin
from .models import Post
# 注册Post模型到Admin站点(装饰器写法)
@admin.register(Post)
class PostAdmin(admin.ModelAdmin):
# 在Admin中显示的字段
list_display = ['title', 'slug', 'publish', 'status']
list_filter = ['status', 'created', 'publish'] # 过滤选项(侧边栏)
search_fields = ['title', 'body'] # 搜索字段
prepopulated_fields = {'slug': ('title',)} # slug字段自动填充
date_hierarchy = 'publish' # 日期分层导航
ordering = ['status', 'publish'] # 默认排序
show_facets = admin.ShowFacets.ALWAYS # 始终显示侧边栏过滤器
# 可选:自定义 Admin 站点标题
admin.site.site_header = '博客管理系统' # 登录页面和后台顶部标题
admin.site.site_title = '博客管理后台' # 浏览器标签页标题
admin.site.index_title = '欢迎使用博客管理系统' # 首页标题中控台驱动后台 UI
Control Panel Drives Admin UI
Search Title/Body...
| □ | TITLE |
|---|---|
| □ | My first post |
| □ | Django Intro |
Filter
By status
- All
- Draft
- Published
By author
- admin
- guest
admin.py `class PostAdmin(admin.ModelAdmin):`
title
slug
publish
status
通过在 `admin.py` 中配置 `ModelAdmin` 的类属性(如 list_display、list_filter),我们就像在推拉中控台上的开关,无需编写前端 HTML,即可驱动庞大复杂的 Admin UI 进行模块化重组。
By configuring class attributes like list_display and list_filter, we act as if pushing switches on a smart panel, directly generating complex Admin UI changes without writing HTML.
编辑 blog/apps.py文件,配置博客应用,具体如下:
from django.apps import AppConfig
# 博客应用配置
class BlogConfig(AppConfig):
# 默认名称(不要改这个)
default_auto_field = 'django.db.models.BigAutoField'
name = 'blog' # 应用名称(保持与创建时一致)
verbose_name = '博客管理' # 在Django Admin中显示的应用名称现在打开浏览器,访问 http://127.0.0.1:8000/admin/
四. 在应用中使用模型
1. 在交互式 Python Shell 中操作
打开 Django 提供的交互式 Python Shell,来实际操作一下模型的增删改查:
python manage.py shell然后输入以下代码:
>>> from blog.models import Post
>>> post = Post(title='Another post',
... slug='another-post',
... body='Post body.')
>>> post.save()更新对象 把刚才创建的 Post 对象的标题改一下,然后重新保存:
>>> post.title = 'New title'
>>> post.save()查询所有对象
>>> Post.objects.all()
<QuerySet [<Post: Who was Django Reinhardt?>, <Post: New title>]>使用字段查找过滤数据 Django 用双下划线来指定查找类型,格式为 field__lookup。比如下面是精确匹配(exact)查找:
>>> Post.objects.filter(id__exact=1)如果不指定查找类型,默认就是 exact。所以下面这行和上面的效果一样:
>>> Post.objects.filter(id=1)下面再看几种常用的查找方式。iexact 是不区分大小写的精确匹配:
>>> Post.objects.filter(title__iexact='who was django reinhardt?')contains 用于模糊查找,判断字段是否包含指定内容,对应 SQL 中的 LIKE 语句:
>>> Post.objects.filter(title__contains='Django')上面这行对应的 SQL 是 WHERE title LIKE '%Django%'。还有一个不区分大小写的版本 icontains:
>>> Post.objects.filter(title__icontains='django')in 查找用于判断字段值是否在给定的列表中。下面的例子查询 id 为 1 或 3 的博文:
>>> Post.objects.filter(id__in=[1, 3])gt 表示大于(greater than),下面的例子查询 id 大于 3 的博文:
>>> Post.objects.filter(id__gt=3)对应的 SQL 是 WHERE ID > 3。 gte 表示大于等于(greater than or equal):
>>> Post.objects.filter(id__gte=3)lt 表示小于(less than):
>>> Post.objects.filter(id__lt=3)lte 表示小于等于(less than or equal):
>>> Post.objects.filter(id__lte=3)查询结果排序 模型 Meta 类中的 ordering 属性定义了默认排序规则。你也可以用 order_by() 方法来自定义排序。比如按 title 升序排列:
>>> Post.objects.order_by('title')不加前缀就是升序,加负号 - 表示降序:
>>> Post.objects.order_by('-title')还可以按多个字段排序,比如先按 author 排,再按 title 排:
>>> Post.objects.order_by('author', 'title')限制查询结果数量 可以用 Python 的切片语法来限制 QuerySet 返回的数量。比如只取前 5 条:
>>> Post.objects.all()[:5]这会生成 SQL 中的 LIMIT 5。注意,不支持负数索引。
>>> Post.objects.all()[3:6]上面这行对应 SQL 中的 OFFSET 3 LIMIT 3,返回第 4 到第 6 条记录。
数据长卷的移动视口
Viewport on Data Scroll (Pagination Limit/Offset)
Database Scroll (Total: 100 Posts)
Post ID: 1 #179
Post ID: 2 #358
Post ID: 3 #537
Post ID: 4 #716
Post ID: 5 #895
Post ID: 6 #1074
Post ID: 7 #1253
Post ID: 8 #1432
Post ID: 9 #1611
Post ID: 10 #1790
Post ID: 11 #1969
Post ID: 12 #2148
Post ID: 13 #2327
Post ID: 14 #2506
Post ID: 15 #2685
Post ID: 16 #2864
Post ID: 17 #3043
Post ID: 18 #3222
Post ID: 19 #3401
Post ID: 20 #3580
Post ID: 21 #3759
Post ID: 22 #3938
Post ID: 23 #4117
Post ID: 24 #4296
Post ID: 25 #4475
Post ID: 26 #4654
Post ID: 27 #4833
Post ID: 28 #5012
Post ID: 29 #5191
Post ID: 30 #5370
Post ID: 31 #5549
Post ID: 32 #5728
Post ID: 33 #5907
Post ID: 34 #6086
Post ID: 35 #6265
Post ID: 36 #6444
Post ID: 37 #6623
Post ID: 38 #6802
Post ID: 39 #6981
Post ID: 40 #7160
Post ID: 41 #7339
Post ID: 42 #7518
Post ID: 43 #7697
Post ID: 44 #7876
Post ID: 45 #8055
Post ID: 46 #8234
Post ID: 47 #8413
Post ID: 48 #8592
Post ID: 49 #8771
Post ID: 50 #8950
Post ID: 51 #9129
Post ID: 52 #9308
Post ID: 53 #9487
Post ID: 54 #9666
Post ID: 55 #9845
Post ID: 56 #25
Post ID: 57 #204
Post ID: 58 #383
Post ID: 59 #562
Post ID: 60 #741
Post ID: 61 #920
Post ID: 62 #1099
Post ID: 63 #1278
Post ID: 64 #1457
Post ID: 65 #1636
Post ID: 66 #1815
Post ID: 67 #1994
Post ID: 68 #2173
Post ID: 69 #2352
Post ID: 70 #2531
Post ID: 71 #2710
Post ID: 72 #2889
Post ID: 73 #3068
Post ID: 74 #3247
Post ID: 75 #3426
Post ID: 76 #3605
Post ID: 77 #3784
Post ID: 78 #3963
Post ID: 79 #4142
Post ID: 80 #4321
Post ID: 81 #4500
Post ID: 82 #4679
Post ID: 83 #4858
Post ID: 84 #5037
Post ID: 85 #5216
Post ID: 86 #5395
Post ID: 87 #5574
Post ID: 88 #5753
Post ID: 89 #5932
Post ID: 90 #6111
Post ID: 91 #6290
Post ID: 92 #6469
Post ID: 93 #6648
Post ID: 94 #6827
Post ID: 95 #7006
Post ID: 96 #7185
Post ID: 97 #7364
Post ID: 98 #7543
Post ID: 99 #7722
Post ID: 100 #7901
Page 1
SELECT * FROM blog_post
ORDER BY publish DESC
LIMIT 5
OFFSET 0; posts = Post.objects.all()
page = 1
posts[ 0 : 5 ] 12345...20
当数据量犹如清明上河图般漫长,前端分页就如同一个“特定尺寸的观景相框”。
When data is vast like a long scroll, pagination acts as a fixed-size viewport frame. Changing the page merely moves the start point (Offset) while keeping the frame size (Limit).
如果只想取单个对象,可以用索引代替切片。比如随机取一条记录:
>>> Post.objects.order_by('?')[0]统计对象数量count() 方法返回 QuerySet 中匹配的对象总数,对应 SQL 中的 SELECT COUNT(*)。下面的例子统计 id 小于 3 的博文数量:
>>> Post.objects.filter(id_lt=3).count()
2判断对象是否存在exists() 方法用来判断 QuerySet 中是否有数据,有则返回 True,否则返回 False。比如检查是否存在标题以"为什么"开头的博文:
>>> Post.objects.filter(title__startswith='Why').exists()
False删除对象 调用对象的 delete() 方法即可删除:
>>> post = Post.objects.get(id=1)
>>> post.delete()2. 通过视图加模板展示博文信息
了解了 ORM 的基本用法后,我们开始构建 blog 应用的视图。Django 视图本质上就是一个 Python 函数,接收一个 Web 请求,返回一个 Web 响应。所有处理逻辑都写在视图函数里。
开发步骤是:先写视图函数,再配置 URL 路由,最后创建 HTML 模板。每个视图渲染一个模板,把数据通过上下文变量传给模板,最终返回渲染好的 HTTP 响应。
完成后的项目目录结构:
mysite/
├── manage.py
├─── mysite
│ ├── __init__.py
│ ├── settings.py
│ ├── urls.py
│ ├── asgi.py
│ └── wsgi.py
└── blog
├── __init__.py
├── admin.py
├── apps.py
├── migrations
│ └── __init__.py
├── static # 静态资源默认目录
│ └── css
│ └── blog.css # css样式文件
├── templates # 默认博客模版目录
│ └── blog
│ ├── base.html # 公共博文父模版文件
│ └── post
│ ├─ list.html # 博文列表模版文件
│ └─ detail.html # 博文详情模版文件
├── urls.py # 自定义blog博客应用路由文件
├── models.py
├── tests.py
└── views.py① 在blog应用中使用视图
编辑
blog/views.py视图文件,定义两个视图函数:博文列表和博文详情。代码如下:
# 导入 Django 的快捷函数
# get_object_or_404: 如果查询对象不存在,自动返回404页面,简化异常处理
# render: 将模板和上下文数据渲染为HTTP响应,是常用的视图函数
from django.shortcuts import get_object_or_404,render
# 从当前目录(.)的 models 模块中导入 Post 模型
from .models import Post
def post_list(request):
# 通过Post模型的objects管理器查询所有文章记录,返回一个QuerySet对象
posts = Post.objects.all()
# 使用render函数渲染模板并返回HTTP响应
return render(
request, # 请求对象,模板中可以使用请求上下文
'blog/post/list.html', # 模板路径(从templates目录开始)
{'posts': posts} # 上下文数据,将posts传递给模板
)
def post_detail(request, id):
# 使用get_object_or_404函数简化查询和异常处理
post = get_object_or_404(
Post, # 要查询的模型类
id=id, # 根据传入的id参数进行查询
status=Post.Status.PUBLISHED # 只查询已发布的文章
)
# 渲染详情页面模板
# 注意:这里只需要传递单篇文章对象,不是列表
return render(
request,
'blog/post/detail.html',
{'post': post}
)② 为视图函数配置URL访问路由
- 在blog应用中创建URL路由文件urls.py,代码如下:
from django.urls import path
from . import views
app_name = 'blog' # 命名空间
urlpatterns = [
# post views
path('', views.post_list, name='post_list'), # 博文列表路由
# 博文详情路由
path('<int:id>/', views.post_detail, name='post_detail'),
]接下来在项目的主路由中引入 blog 应用的路由配置。
编辑 mysite/urls.py 文件,代码如下:
from django.contrib import admin
from django.urls import include, path # 导入 include 函数
urlpatterns = [
path('admin/', admin.site.urls),
path('blog/', include('blog.urls')), # 包含blog应用的URL配置
]③ 创建模板文件
- 在
blog应用目录下创建以下目录和文件:
├── templates
├─ blog
├── base.html
└── post
├─ list.html
└─ detail.html- base.html模板文件代码:
{% load static %}
<!DOCTYPE html>
<html>
<head>
<title>{% block title %}{% endblock %}</title>
<link href="{% static "css/blog.css" %}" rel="stylesheet">
</head>
<body>
<div id="content">
{% block content %}
{% endblock %}
</div>
<div id="sidebar">
<h2>程序员的小屋</h2>
<br/>
<p>键盘敲击代码,墨痕书写思考。这是一个程序员的人文技术博客,在0和1的世界里寻找诗意,在算法与架构中发现美感。</p>
</div>
</body>
</html>- list.html模板文件代码:
{% extends "blog/base.html" %}
{% block title %}程序员的小屋{% endblock %}
{% block content %}
<h1>程序员的小屋</h1>
{% for post in posts %}
<h2>
<a href="{% url 'blog:post_detail' post.id %}">
{{ post.title }}
</a>
</h2>
<p class="date">
发布时间: {{ post.publish }}
</p>
{{ post.body|truncatewords:30|linebreaks }}
{% endfor %}
{% endblock %}- detail.html模板文件代码:
{% extends "blog/base.html" %}
{% block title %}{{ post.title }}{% endblock %}
{% block content %}
<h1>{{ post.title }}</h1>
<p class="date">
发布时间: {{ post.publish }}
</p>
{{ post.body|linebreaks }}
{% endblock %}模板继承的预制填缺
Template Inheritance Assembly
Child Templates (子模板)
点击子模板插入母板插槽
list.html
{% block content %}
[ 博客列表清单 ]
{% endblock %}
[ 博客列表清单 ]
{% endblock %}
detail.html
{% block content %}
[ 文章详细图文 ]
{% endblock %}
[ 文章详细图文 ]
{% endblock %}
{% extends %}
**base.html** (预制母板)
<html>
<header> 这里是全站统一导航/标语 </header>
预留内容插槽
{% block content %}
{% block content %}
<footer> 这里是全站统一版权信息 </footer>
</html>
base.html 提供了一个具有骨架与透明孔洞(block content)的托盘。各个视图响应对应的不同子模板只需写自己要**填充缺口**的代码即可。
The base template provides a structural tray with empty slots. Child templates merely declare which blocks to fill, drastically reducing HTML duplication.
- 添加样式blog.css 文件:blog.css
└── blog
├── static
│ └── css
│ └── blog.css④ 运行开发服务器
启动服务:
$ python manage.py runserver
或
$ python manage.py runserver 0.0.0.0:8000打开浏览器,访问 http://127.0.0.1:8000/blog/ 博文列表页
博文详情页:

- 最终课堂案例代码:mysite.zip
五、请求/响应周期
下面我们结合博客应用,来看看 Django 的请求/响应周期是怎么工作的。下图展示了 Django 处理一次 HTTP 请求并返回响应的简化流程:
具体流程如下:
浏览器通过 URL 发起页面请求,比如
https://domain.com/blog/33/。Web 服务器收到这个 HTTP 请求后,交给 Django 处理。Django 按顺序逐个匹配 URL 配置中定义的路由规则,找到第一个匹配的就停下来。在这个例子中,路由规则
/blog/<id>/匹配了请求路径/blog/33/。Django 加载匹配到的视图函数并执行,同时传入
HttpRequest对象和 URL 中解析出的参数。视图函数通过模型从数据库查询数据,Django ORM 会把 QuerySet 翻译成 SQL 语句并在数据库中执行。视图函数调用
render()渲染 HTML 模板,把Post对象作为上下文变量传给模板。渲染完成后,视图返回一个
HttpResponse对象,默认的内容类型是text/html。
请求走珠的生命迷宫
Request Marble's Life Cycle
🌐 Browser
Request
Request
🚦 urls.py
👨💻 View
get_object_or_404()
get_object_or_404()
💾 Database
SQLite
SQLite
🎨 Template
HTML
HTML
💀 404 Page
点击任意按钮发射请求滚珠,观察 Django 生命周期。
Launch a request marble to observe the Django life cycle.
