之前我们已经学过用 DRF 开发前后端分离的 Django 项目了,现在我们补充一下前后端不分离的古法 Web 开发的知识点。
1. Bootstrap 5
Bootstrap 是目前非常常用的前端 CSS 框架。它可以帮助我们快速开发美观、整齐、响应式的网页,而不需要自己编写大量 CSS 样式。
如果把 HTML 理解为网页的骨架,那么 Bootstrap 就可以理解为网页的装修工具。
学习 Bootstrap 之后,我们可以使用现成的样式类快速制作:
- 登录页面
- 导航栏
- 后台管理系统
- 数据列表
- 表单
- 弹窗
- 分页组件
- 响应式网页
Bootstrap 主要通过 HTML 的 class 属性来使用。例如:
html
<!-- 普通 HTML 按钮 -->
<button>提交</button>
<!-- Bootstrap 按钮 -->
<button class="btn btn-primary">提交</button>
第二个按钮只需要添加两个 CSS 类,就能获得 Bootstrap 提供的按钮样式。
1.1 Bootstrap 简介与环境配置
1.1.1 Bootstrap 的作用
Bootstrap 是一个前端 UI 框架,主要提供:
- CSS 样式: 按钮、表格、表单、颜色、间距等。
- 页面布局: 容器、栅格系统、响应式布局等。
- UI 组件: 导航栏、卡片、模态框、分页、下拉菜单等。
- JavaScript 交互: 折叠菜单、模态框、下拉菜单、轮播图等。
Bootstrap 5 的一个重要特点是:不再依赖 jQuery,可以直接使用原生 JavaScript 实现组件交互。
1.1.2 CDN 引入
CDN 是一种网络资源分发服务。
使用 CDN 可以直接从网络加载 Bootstrap 文件,不需要自己下载和保存 Bootstrap 文件。
在 HTML 的 head 中引入 Bootstrap CSS,在 body 结束标签前引入 Bootstrap JavaScript:
html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Bootstrap 学习</title>
<!-- 引入 Bootstrap CSS -->
<link
href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.8/dist/css/bootstrap.min.css"
rel="stylesheet"
>
</head>
<body>
<h1 class="text-primary">Hello Bootstrap!</h1>
<button class="btn btn-primary">点击我</button>
<!-- 引入 Bootstrap JavaScript(包含常用组件所需的 Popper) -->
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.8/dist/js/bootstrap.bundle.min.js"></script>
</body>
</html>
注意:
- CSS 文件负责提供页面样式。
- JavaScript 文件负责提供交互功能。
bootstrap.bundle.min.js已经包含 Popper,不需要额外引入 Popper。viewport用于让网页正确适配手机屏幕。
1.1.3 本地文件引入
如果不想依赖 CDN,也可以下载 Bootstrap 文件,放在项目本地。
例如:
my_project/
│
├── index.html
│
├── static/
│ ├── css/
│ │ └── bootstrap.min.css
│ │
│ └── js/
│ └── bootstrap.bundle.min.js
HTML 引入方式:
html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Bootstrap 本地引入</title>
<!-- 引入本地 CSS -->
<link rel="stylesheet" href="static/css/bootstrap.min.css">
</head>
<body>
<button class="btn btn-success">本地 Bootstrap</button>
<!-- 引入本地 JavaScript -->
<script src="static/js/bootstrap.bundle.min.js"></script>
</body>
</html>
如果使用 Django,通常会将 Bootstrap 文件放入 Django 的 static 静态文件目录中,然后通过 {% static %} 引入。
1.2 容器与页面布局
Bootstrap 提供了容器和栅格系统,可以帮助我们快速完成页面布局。
1.2.1 container
container 是 Bootstrap 提供的响应式固定宽度容器。
它会根据屏幕尺寸自动调整最大宽度,并且在页面中居中显示。
html
<div class="container">
<h2>页面内容</h2>
<p>这是一个 container 容器。</p>
</div>
特点:
- 在大屏幕上限制内容最大宽度。
- 在小屏幕上自动适应屏幕宽度。
- 默认左右居中。
- 不同屏幕尺寸对应不同的最大宽度。
1.2.2 container-fluid
container-fluid 是一个宽度始终为父级宽度 100% 的容器。
html
<div class="container-fluid bg-primary text-white">
<h2>全宽容器</h2>
<p>这个容器会占据整个可用宽度。</p>
</div>
两者区别:
| 类名 | 特点 |
|---|---|
container |
响应式固定最大宽度 |
container-fluid |
始终占据 100% 可用宽度 |
1.2.3 栅格系统
Bootstrap 使用 12 列栅格系统来布局网页。
可以把网页的横向空间看成 12 份,通过 col-* 指定每个元素占据多少份。
例如:
html
<div class="container">
<div class="row">
<!-- 占据 12 列,即整行 -->
<div class="col-12 bg-primary text-white p-3">
12 列
</div>
</div>
<div class="row mt-3">
<!-- 每个元素占据 6 列 -->
<div class="col-6 bg-success text-white p-3">
6 列
</div>
<div class="col-6 bg-warning p-3">
6 列
</div>
</div>
<div class="row mt-3">
<!-- 每个元素占据 4 列 -->
<div class="col-4 bg-danger text-white p-3">
4 列
</div>
<div class="col-4 bg-info p-3">
4 列
</div>
<div class="col-4 bg-secondary text-white p-3">
4 列
</div>
</div>
</div>
注意:通常使用 row 包裹 col,这样 Bootstrap 才能正确处理列的排列和间距。
1.2.4 row 与 col
row 表示一行,col 表示一列。
最简单的写法:
html
<div class="container">
<div class="row">
<div class="col">
内容一
</div>
<div class="col">
内容二
</div>
<div class="col">
内容三
</div>
</div>
</div>
这里没有指定列宽,Bootstrap 会自动平均分配空间。
如果有三个 col,每个元素就会占据大约三分之一的宽度。
也可以指定列宽:
html
<div class="row">
<div class="col-3">左侧 3 列</div>
<div class="col-6">中间 6 列</div>
<div class="col-3">右侧 3 列</div>
</div>
总宽度为:
3 + 6 + 3 = 12
1.2.5 响应式断点
响应式布局是指网页能够根据不同屏幕尺寸自动调整布局。
Bootstrap 5 提供以下断点:
| 前缀 | 屏幕宽度 | 设备示例 |
|---|---|---|
| 无前缀 | 小于 576px | 手机 |
sm |
≥576px | 大屏手机 |
md |
≥768px | 平板 |
lg |
≥992px | 笔记本 |
xl |
≥1200px | 桌面显示器 |
xxl |
≥1400px | 大屏显示器 |
例如:
html
<div class="container">
<div class="row">
<!-- 手机:12列;平板及以上:6列;大屏:4列 -->
<div class="col-12 col-md-6 col-lg-4">
内容一
</div>
<div class="col-12 col-md-6 col-lg-4">
内容二
</div>
<div class="col-12 col-md-6 col-lg-4">
内容三
</div>
</div>
</div>
这段代码的效果:
- 手机:每行显示一个内容。
- 平板:每行显示两个内容。
- 大屏:每行显示三个内容。
记住这个规律:
col-12 手机占 12 列
col-md-6 屏幕达到 768px 后占 6 列
col-lg-4 屏幕达到 992px 后占 4 列
1.3 间距与尺寸
Bootstrap 提供了一套工具类,可以直接控制元素的外边距、内边距、宽度、高度和间距。
不需要每次都编写 CSS。
1.3.1 Margin(外边距)
Margin 用于设置元素外部的间距。
Bootstrap 使用 m 表示 Margin。
常用格式:
m-数字 四个方向的外边距
mt-数字 上外边距
mb-数字 下外边距
ms-数字 左侧外边距
me-数字 右侧外边距
mx-数字 左右外边距
my-数字 上下外边距
其中:
t:top,上。b:bottom,下。s:start,起始方向。e:end,结束方向。x:水平方向。y:垂直方向。
注意:ms 和 me 使用逻辑方向,默认从左到右的页面中分别对应左边和右边。
Bootstrap 常用间距值:
| 数字 | 对应大小 |
|---|---|
0 |
0 |
1 |
0.25rem |
2 |
0.5rem |
3 |
1rem |
4 |
1.5rem |
5 |
3rem |
例如:
html
<div class="m-3">四周外边距 1rem</div>
<div class="mt-3">上边距 1rem</div>
<div class="mb-4">下边距 1.5rem</div>
<div class="mx-auto">左右自动居中</div>
<div class="my-5">上下边距 3rem</div>
1.3.2 Padding(内边距)
Padding 用于设置元素内部内容与边框之间的距离。
Bootstrap 使用 p 表示 Padding。
格式与 Margin 类似:
p-数字 四个方向的内边距
pt-数字 上内边距
pb-数字 下内边距
ps-数字 左侧内边距
pe-数字 右侧内边距
px-数字 左右内边距
py-数字 上下内边距
示例:
html
<div class="p-3 bg-primary text-white">
四周内边距
</div>
<div class="px-4 bg-success text-white">
左右内边距
</div>
<div class="py-5 bg-warning">
上下内边距
</div>
1.3.3 Width / Height(宽度和高度)
Bootstrap 提供了常用的宽度和高度工具类。
宽度:
| 类名 | 效果 |
|---|---|
w-25 |
宽度 25% |
w-50 |
宽度 50% |
w-75 |
宽度 75% |
w-100 |
宽度 100% |
w-auto |
自动宽度 |
高度:
| 类名 | 效果 |
|---|---|
h-25 |
高度 25% |
h-50 |
高度 50% |
h-75 |
高度 75% |
h-100 |
高度 100% |
h-auto |
自动高度 |
示例:
html
<div class="w-25 bg-primary text-white p-2">宽度 25%</div>
<div class="w-50 bg-success text-white p-2">宽度 50%</div>
<div class="w-100 bg-danger text-white p-2">宽度 100%</div>
注意:百分比高度通常需要父元素具有明确的高度,否则可能无法得到预期效果。
还可以使用:
html
<div class="min-vh-100">
最小高度为视口高度的 100%
</div>
vh 表示视口高度,min-vh-100 常用于让页面至少占满整个屏幕。
1.3.4 Gap(元素间距)
Gap 用于设置 Flex 或 Grid 布局中子元素之间的间距。
常用类:
gap-0
gap-1
gap-2
gap-3
gap-4
gap-5
示例:
html
<!-- d-flex 将容器设置为 Flex 布局 -->
<div class="d-flex gap-3">
<button class="btn btn-primary">按钮一</button>
<button class="btn btn-success">按钮二</button>
<button class="btn btn-danger">按钮三</button>
</div>
这里三个按钮之间会自动产生间距。
也可以使用:
html
<div class="d-flex flex-column gap-3">
<div class="p-3 bg-primary text-white">内容一</div>
<div class="p-3 bg-success text-white">内容二</div>
<div class="p-3 bg-danger text-white">内容三</div>
</div>
flex-column 表示子元素纵向排列。
1.4 文字与颜色
Bootstrap 提供了很多文字和颜色工具类,可以快速美化页面。
1.4.1 文字大小
Bootstrap 提供 fs-* 设置字体大小。
html
<p class="fs-1">字体大小 fs-1</p>
<p class="fs-2">字体大小 fs-2</p>
<p class="fs-3">字体大小 fs-3</p>
<p class="fs-4">字体大小 fs-4</p>
<p class="fs-5">字体大小 fs-5</p>
<p class="fs-6">字体大小 fs-6</p>
数字越小,字体越大。
另外,Bootstrap 还提供标题类:
html
<p class="h1">类似 h1 的文字</p>
<p class="h2">类似 h2 的文字</p>
<p class="h3">类似 h3 的文字</p>
注意:h1、h2 是 HTML 标题标签,而 class="h1" 只是应用类似标题的样式,两者并不相同。
1.4.2 文字颜色
常用文字颜色:
| 类名 | 颜色 |
|---|---|
text-primary |
主题蓝色 |
text-secondary |
灰色 |
text-success |
绿色 |
text-danger |
红色 |
text-warning |
黄色 |
text-info |
青色 |
text-light |
浅色 |
text-dark |
深色 |
text-white |
白色 |
text-muted |
柔和灰色 |
示例:
html
<p class="text-primary">蓝色文字</p>
<p class="text-success">绿色文字</p>
<p class="text-danger">红色文字</p>
<p class="text-warning">黄色文字</p>
<p class="text-muted">灰色辅助文字</p>
1.4.3 背景颜色
常用背景颜色:
| 类名 | 颜色 |
|---|---|
bg-primary |
蓝色 |
bg-secondary |
灰色 |
bg-success |
绿色 |
bg-danger |
红色 |
bg-warning |
黄色 |
bg-info |
青色 |
bg-light |
浅灰色 |
bg-dark |
深灰色 |
bg-white |
白色 |
示例:
html
<div class="bg-primary text-white p-3">
蓝色背景
</div>
<div class="bg-success text-white p-3">
绿色背景
</div>
<div class="bg-light text-dark p-3">
浅灰色背景
</div>
1.4.4 文字对齐
使用 text-* 设置文字对齐方式:
html
<p class="text-start">左对齐</p>
<p class="text-center">居中对齐</p>
<p class="text-end">右对齐</p>
也可以设置响应式对齐:
html
<!-- 默认左对齐,屏幕达到 md 后居中 -->
<p class="text-start text-md-center">
响应式文字对齐
</p>
1.4.5 字体粗细
使用以下类控制字体粗细:
| 类名 | 效果 |
|---|---|
fw-bold |
加粗 |
fw-bolder |
更粗 |
fw-semibold |
半粗 |
fw-normal |
正常 |
fw-light |
细体 |
fw-lighter |
更细 |
示例:
html
<p class="fw-bold">加粗文字</p>
<p class="fw-normal">正常文字</p>
<p class="fw-light">细体文字</p>
<p class="fw-semibold">半粗文字</p>
1.5 按钮
Bootstrap 提供了多种现成的按钮样式。
1.5.1 按钮样式
使用 btn 类创建 Bootstrap 按钮。
html
<button class="btn btn-primary">主要按钮</button>
<button class="btn btn-secondary">次要按钮</button>
<button class="btn btn-success">成功按钮</button>
<button class="btn btn-danger">危险按钮</button>
<button class="btn btn-warning">警告按钮</button>
<button class="btn btn-info">信息按钮</button>
<button class="btn btn-light">浅色按钮</button>
<button class="btn btn-dark">深色按钮</button>
注意:
btn是按钮的基础样式。btn-primary等类用于指定按钮颜色和主题。
1.5.2 按钮颜色
Bootstrap 还提供轮廓按钮,即只有边框和文字有颜色,背景默认透明。
html
<button class="btn btn-outline-primary">蓝色轮廓</button>
<button class="btn btn-outline-success">绿色轮廓</button>
<button class="btn btn-outline-danger">红色轮廓</button>
<button class="btn btn-outline-dark">黑色轮廓</button>
1.5.3 按钮尺寸
使用 btn-lg 和 btn-sm 控制按钮大小。
html
<button class="btn btn-primary btn-lg">大按钮</button>
<button class="btn btn-primary">默认按钮</button>
<button class="btn btn-primary btn-sm">小按钮</button>
如果想让按钮占满父容器宽度:
html
<button class="btn btn-primary w-100">
占满宽度
</button>
1.5.4 按钮状态
按钮常见状态包括:
禁用状态:
html
<button class="btn btn-primary" disabled>
禁用按钮
</button>
disabled 是 HTML 原生属性,不是 Bootstrap 专属属性。
加载状态:
html
<button class="btn btn-primary" disabled>
<!-- 加载动画 -->
<span class="spinner-border spinner-border-sm"></span>
正在提交...
</button>
激活状态:
html
<button class="btn btn-primary active">
激活状态
</button>
链接按钮:
html
<a href="/home/" class="btn btn-primary">
返回首页
</a>
1.6 导航栏
导航栏(Navbar)通常放在网页顶部,用于展示网站名称、首页、关于我们、登录等入口。
1.6.1 Navbar
最简单的导航栏:
html
<nav class="navbar bg-light">
<div class="container">
<!-- 网站名称 -->
<a class="navbar-brand" href="#">
My Website
</a>
<!-- 导航链接 -->
<div class="navbar-nav flex-row gap-3">
<a class="nav-link active" href="#">首页</a>
<a class="nav-link" href="#">文章</a>
<a class="nav-link" href="#">关于我们</a>
</div>
</div>
</nav>
常见类:
| 类名 | 作用 |
|---|---|
navbar |
导航栏容器 |
navbar-brand |
网站名称或 Logo |
navbar-nav |
导航链接容器 |
nav-link |
导航链接 |
active |
当前激活的链接 |
navbar-toggler |
折叠菜单按钮 |
navbar-collapse |
可折叠内容区域 |
1.6.2 导航链接
可以使用 Bootstrap 的颜色类设置导航栏背景和文字颜色。
html
<nav class="navbar navbar-dark bg-dark">
<div class="container">
<a class="navbar-brand" href="#">
My Website
</a>
<div class="navbar-nav flex-row gap-3">
<a class="nav-link active" href="#">首页</a>
<a class="nav-link" href="#">产品</a>
<a class="nav-link" href="#">联系我们</a>
</div>
</div>
</nav>
navbar-dark 用于深色背景下的导航栏内容配色。在 Bootstrap 5.3 中,更推荐使用 data-bs-theme="dark" 控制组件主题。
1.6.3 折叠菜单
当导航栏空间不足时,可以将导航链接折叠起来,通过按钮展开。
Bootstrap 使用 navbar-toggler 和 collapse 实现这个功能。
html
<nav class="navbar navbar-expand-lg bg-dark" data-bs-theme="dark">
<div class="container">
<!-- 网站名称 -->
<a class="navbar-brand" href="#">My Website</a>
<!-- 手机端菜单按钮 -->
<button
class="navbar-toggler"
type="button"
data-bs-toggle="collapse"
data-bs-target="#navbarMenu"
aria-controls="navbarMenu"
aria-expanded="false"
aria-label="切换导航菜单"
>
<span class="navbar-toggler-icon"></span>
</button>
<!-- 可折叠菜单 -->
<div class="collapse navbar-collapse" id="navbarMenu">
<div class="navbar-nav ms-auto">
<a class="nav-link active" href="#">首页</a>
<a class="nav-link" href="#">文章</a>
<a class="nav-link" href="#">关于我们</a>
</div>
</div>
</div>
</nav>
这里有几个重要知识点:
navbar-expand-lg:屏幕宽度达到 992px 时展开导航栏。navbar-toggler:折叠菜单按钮。data-bs-toggle="collapse":告诉 Bootstrap 这是一个折叠控制按钮。data-bs-target="#navbarMenu":指定需要控制的元素。id="navbarMenu":被控制元素的唯一标识。ms-auto:将导航链接推到右侧。
1.6.4 响应式导航栏
上面的代码已经是响应式导航栏。
效果:
- 桌面端:导航链接横向显示。
- 手机端:导航链接默认隐藏,点击按钮展开。
- 不需要自己编写 JavaScript。
注意:折叠菜单需要提前引入 bootstrap.bundle.min.js,否则按钮无法正常工作。
1.7 卡片
Card 是 Bootstrap 中非常常用的组件,可以用来展示文章、商品、用户信息、统计数据等。
1.7.1 Card
最简单的卡片:
html
<div class="card" style="width: 18rem;">
<div class="card-body">
<h5 class="card-title">卡片标题</h5>
<p class="card-text">
这里是卡片的内容,可以放置文字说明。
</p>
<a href="#" class="btn btn-primary">
查看详情
</a>
</div>
</div>
常用类:
| 类名 | 作用 |
|---|---|
card |
卡片容器 |
card-body |
卡片主体 |
card-title |
卡片标题 |
card-text |
卡片文字 |
card-img-top |
卡片顶部图片 |
card-link |
卡片链接 |
1.7.2 Card Header
Header 是卡片顶部区域,通常用于放置标题。
html
<div class="card">
<div class="card-header">
用户信息
</div>
<div class="card-body">
<p>用户名:Tom</p>
<p>年龄:20</p>
</div>
</div>
1.7.3 Card Body
Body 是卡片的主要内容区域。
html
<div class="card">
<div class="card-body">
<h5 class="card-title">欢迎使用</h5>
<p class="card-text">
这是卡片主体内容。
</p>
</div>
</div>
1.7.4 Card Footer
Footer 是卡片底部区域,可以放置备注、时间、操作按钮等。
html
<div class="card">
<div class="card-header">
文章信息
</div>
<div class="card-body">
<h5 class="card-title">Bootstrap 入门</h5>
<p class="card-text">
学习如何使用 Bootstrap 快速构建网页。
</p>
</div>
<div class="card-footer text-muted">
发布时间:2026-11-11
</div>
</div>
1.8 表格
Bootstrap 可以快速美化 HTML 原生表格。
1.8.1 基础表格
给 <table> 添加 table 类即可。
html
<table class="table">
<thead>
<tr>
<th>ID</th>
<th>用户名</th>
<th>年龄</th>
<th>城市</th>
</tr>
</thead>
<tbody>
<tr>
<td>1</td>
<td>Tom</td>
<td>20</td>
<td>北京</td>
</tr>
<tr>
<td>2</td>
<td>Jack</td>
<td>25</td>
<td>上海</td>
</tr>
</tbody>
</table>
1.8.2 条纹表格
使用 table-striped 可以让表格的行交替显示不同背景颜色。
html
<table class="table table-striped">
<thead>
<tr>
<th>ID</th>
<th>用户名</th>
</tr>
</thead>
<tbody>
<tr>
<td>1</td>
<td>Tom</td>
</tr>
<tr>
<td>2</td>
<td>Jack</td>
</tr>
<tr>
<td>3</td>
<td>Lucy</td>
</tr>
</tbody>
</table>
1.8.3 悬停效果
使用 table-hover,鼠标移动到某一行时会显示悬停效果。
html
<table class="table table-hover">
<tr>
<th>ID</th>
<th>用户名</th>
</tr>
<tr>
<td>1</td>
<td>Tom</td>
</tr>
<tr>
<td>2</td>
<td>Jack</td>
</tr>
</table>
1.8.4 边框与颜色
常用表格类:
| 类名 | 作用 |
|---|---|
table-bordered |
显示所有单元格边框 |
table-borderless |
去掉表格边框 |
table-dark |
深色表格 |
table-primary |
蓝色主题表格 |
table-success |
绿色主题表格 |
table-danger |
红色主题表格 |
table-sm |
缩小表格内边距 |
示例:
html
<table class="table table-bordered table-hover">
<thead class="table-dark">
<tr>
<th>ID</th>
<th>用户名</th>
<th>状态</th>
</tr>
</thead>
<tbody>
<tr>
<td>1</td>
<td>Tom</td>
<td class="table-success">正常</td>
</tr>
<tr>
<td>2</td>
<td>Jack</td>
<td class="table-danger">禁用</td>
</tr>
</tbody>
</table>
1.8.5 响应式表格
当表格列数很多时,手机屏幕可能无法完整显示。
可以使用 table-responsive 让表格在小屏幕上支持横向滚动。
html
<div class="table-responsive">
<table class="table table-bordered">
<thead>
<tr>
<th>ID</th>
<th>用户名</th>
<th>邮箱</th>
<th>手机号</th>
<th>地址</th>
<th>注册时间</th>
</tr>
</thead>
<tbody>
<tr>
<td>1</td>
<td>Tom</td>
<td>tom@example.com</td>
<td>13800000000</td>
<td>北京市</td>
<td>2026-11-11</td>
</tr>
</tbody>
</table>
</div>
1.9 表单
表单是网页中最重要的组成部分之一。
例如:
- 用户登录
- 用户注册
- 搜索功能
- 数据新增
- 数据编辑
- 用户资料修改
Bootstrap 提供了大量表单样式。
1.9.1 输入框
使用 form-control 美化输入框。
html
<div class="mb-3">
<label class="form-label">用户名</label>
<input
type="text"
class="form-control"
placeholder="请输入用户名"
>
</div>
<div class="mb-3">
<label class="form-label">密码</label>
<input
type="password"
class="form-control"
placeholder="请输入密码"
>
</div>
常用类:
| 类名 | 作用 |
|---|---|
form-label |
表单标签 |
form-control |
输入框 |
form-control-lg |
大尺寸输入框 |
form-control-sm |
小尺寸输入框 |
form-text |
辅助提示文字 |
1.9.2 下拉选择框
使用 form-select 美化 <select>。
html
<div class="mb-3">
<label class="form-label">选择城市</label>
<select class="form-select">
<option value="">请选择城市</option>
<option value="beijing">北京</option>
<option value="shanghai">上海</option>
<option value="guangzhou">广州</option>
</select>
</div>
1.9.3 单选框
使用 form-check 和 form-check-input。
html
<div class="form-check">
<input
class="form-check-input"
type="radio"
name="gender"
id="male"
value="male"
checked
>
<label class="form-check-label" for="male">
男
</label>
</div>
<div class="form-check">
<input
class="form-check-input"
type="radio"
name="gender"
id="female"
value="female"
>
<label class="form-check-label" for="female">
女
</label>
</div>
注意:两个单选框的 name 必须相同,才能保证它们属于同一组,用户只能选择其中一个。
1.9.4 复选框
复选框允许同时选择多个选项。
html
<div class="form-check">
<input
class="form-check-input"
type="checkbox"
id="agree"
checked
>
<label class="form-check-label" for="agree">
我同意用户协议
</label>
</div>
<div class="form-check">
<input
class="form-check-input"
type="checkbox"
id="news"
>
<label class="form-check-label" for="news">
接收新闻通知
</label>
</div>
1.9.5 表单布局
可以使用栅格系统将表单排列成多列。
html
<div class="container">
<form>
<div class="row">
<!-- 左侧 -->
<div class="col-md-6 mb-3">
<label class="form-label">用户名</label>
<input type="text" class="form-control">
</div>
<!-- 右侧 -->
<div class="col-md-6 mb-3">
<label class="form-label">邮箱</label>
<input type="email" class="form-control">
</div>
<!-- 占据整行 -->
<div class="col-12 mb-3">
<label class="form-label">个人简介</label>
<textarea class="form-control" rows="4"></textarea>
</div>
</div>
<button type="submit" class="btn btn-primary">
提交
</button>
</form>
</div>
手机端两个输入框会纵向排列,屏幕达到 md 断点后会横向排列。
1.9.6 表单验证样式
Bootstrap 提供 is-valid 和 is-invalid 来显示输入框的验证状态。
html
<div class="mb-3">
<label class="form-label">用户名</label>
<input
type="text"
class="form-control is-valid"
value="Tom"
>
<div class="valid-feedback">
用户名可以使用!
</div>
</div>
<div class="mb-3">
<label class="form-label">邮箱</label>
<input
type="email"
class="form-control is-invalid"
value="错误邮箱"
>
<div class="invalid-feedback">
请输入正确的邮箱地址!
</div>
</div>
注意:
is-valid:显示验证通过样式。is-invalid:显示验证失败样式。valid-feedback:验证通过时的提示。invalid-feedback:验证失败时的提示。
这些类主要负责显示样式,并不会自动完成所有业务验证。实际项目中还需要 JavaScript 或后端进行验证。
1.10 模态框(Modal)
模态框是一种弹出式窗口,通常用于展示额外信息、确认操作或者填写表单。
例如:
- 点击登录,弹出登录窗口。
- 点击删除,弹出确认窗口。
- 点击新增,弹出数据录入窗口。
1.10.1 Modal
Bootstrap 使用 modal 创建模态框。
html
<!-- 打开模态框的按钮 -->
<button
class="btn btn-primary"
data-bs-toggle="modal"
data-bs-target="#myModal"
>
打开模态框
</button>
<!-- 模态框 -->
<div class="modal fade" id="myModal" tabindex="-1">
<div class="modal-dialog">
<div class="modal-content">
<!-- 标题区域 -->
<div class="modal-header">
<h5 class="modal-title">提示信息</h5>
<!-- 右上角关闭按钮 -->
<button
type="button"
class="btn-close"
data-bs-dismiss="modal"
></button>
</div>
<!-- 内容区域 -->
<div class="modal-body">
这是一个 Bootstrap 模态框。
</div>
<!-- 底部区域 -->
<div class="modal-footer">
<button
type="button"
class="btn btn-secondary"
data-bs-dismiss="modal"
>
关闭
</button>
<button type="button" class="btn btn-primary">
确定
</button>
</div>
</div>
</div>
</div>
常用类:
| 类名 | 作用 |
|---|---|
modal |
模态框容器 |
modal-dialog |
模态框主体容器 |
modal-content |
模态框内容 |
modal-header |
标题区域 |
modal-title |
标题 |
modal-body |
内容区域 |
modal-footer |
底部区域 |
modal-dialog-centered |
垂直居中 |
modal-lg |
大尺寸模态框 |
modal-sm |
小尺寸模态框 |
1.10.2 打开与关闭
Bootstrap 支持通过 HTML 属性控制模态框。
打开模态框:
html
<button
data-bs-toggle="modal"
data-bs-target="#myModal"
>
打开
</button>
关闭模态框:
html
<button data-bs-dismiss="modal">
关闭
</button>
如果希望通过 JavaScript 控制,可以使用 Bootstrap 提供的 Modal 对象:
html
<button id="openBtn" class="btn btn-primary">
打开模态框
</button>
<div class="modal fade" id="myModal" tabindex="-1">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title">JavaScript 控制</h5>
<button
class="btn-close"
data-bs-dismiss="modal"
></button>
</div>
<div class="modal-body">
通过 JavaScript 打开模态框。
</div>
</div>
</div>
</div>
<script>
// 获取模态框元素
const modalElement = document.getElementById("myModal");
// 创建 Modal 对象
const myModal = new bootstrap.Modal(modalElement);
// 点击按钮打开模态框
document.getElementById("openBtn").addEventListener("click", function () {
myModal.show();
});
// 关闭模态框
// myModal.hide();
</script>
注意:bootstrap 对象来自 Bootstrap JavaScript 文件,因此必须先引入 bootstrap.bundle.min.js。
1.10.3 模态框表单
实际开发中,经常在模态框里面放置表单。
例如,点击新增用户,弹出用户信息填写窗口。
html
<button
class="btn btn-primary"
data-bs-toggle="modal"
data-bs-target="#addUserModal"
>
新增用户
</button>
<div class="modal fade" id="addUserModal" tabindex="-1">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title">新增用户</h5>
<button
class="btn-close"
data-bs-dismiss="modal"
></button>
</div>
<div class="modal-body">
<form id="userForm">
<div class="mb-3">
<label class="form-label">用户名</label>
<input
type="text"
name="username"
class="form-control"
required
>
</div>
<div class="mb-3">
<label class="form-label">邮箱</label>
<input
type="email"
name="email"
class="form-control"
required
>
</div>
</form>
</div>
<div class="modal-footer">
<button
class="btn btn-secondary"
data-bs-dismiss="modal"
>
取消
</button>
<button
type="submit"
form="userForm"
class="btn btn-primary"
>
保存
</button>
</div>
</div>
</div>
</div>
这里有一个值得注意的地方:
html
<button type="submit" form="userForm">
保存
</button>
form 属性可以让按钮关联指定 ID 的表单,即使按钮不在 <form> 标签内部,也可以提交对应表单。
1.11 其他常用组件
1.11.1 Alert(提示框)
Alert 用于向用户展示提示信息,例如成功、失败、警告等。
常用类:
| 类名 | 作用 |
|---|---|
alert |
提示框基础样式 |
alert-primary |
蓝色提示 |
alert-success |
成功提示 |
alert-danger |
错误提示 |
alert-warning |
警告提示 |
alert-info |
信息提示 |
alert-dismissible |
允许关闭 |
fade |
淡出动画 |
show |
显示状态 |
示例:
html
<div class="alert alert-success">
数据保存成功!
</div>
<div class="alert alert-danger">
用户名或密码错误!
</div>
<div class="alert alert-warning">
请注意填写完整信息!
</div>
可关闭的提示框:
html
<div class="alert alert-success alert-dismissible fade show">
数据保存成功!
<!-- 关闭按钮 -->
<button
type="button"
class="btn-close"
data-bs-dismiss="alert"
></button>
</div>
点击右侧关闭按钮,提示框就会消失。
1.11.2 Badge(徽章)
Badge 用于展示状态、数量、分类等信息。
例如:
- 未读消息数量。
- 用户状态。
- 订单状态。
- 文章分类。
示例:
html
<h3>
消息通知
<span class="badge bg-danger">5</span>
</h3>
<span class="badge bg-success">正常</span>
<span class="badge bg-danger">禁用</span>
<span class="badge bg-warning text-dark">待审核</span>
<span class="badge bg-primary">管理员</span>
也可以使用圆角徽章:
html
<span class="badge rounded-pill bg-primary">
New
</span>
<span class="badge rounded-pill bg-danger">
99+
</span>
1.11.3 Pagination(分页)
Pagination 用于将大量数据分成多个页面。
例如,一个用户列表有 1000 条数据,每页只显示 10 条,就需要分页功能。
示例:
html
<nav aria-label="分页导航">
<ul class="pagination">
<!-- 上一页 -->
<li class="page-item disabled">
<a class="page-link" href="#">上一页</a>
</li>
<!-- 页码 -->
<li class="page-item active">
<a class="page-link" href="#">1</a>
</li>
<li class="page-item">
<a class="page-link" href="#">2</a>
</li>
<li class="page-item">
<a class="page-link" href="#">3</a>
</li>
<!-- 下一页 -->
<li class="page-item">
<a class="page-link" href="#">下一页</a>
</li>
</ul>
</nav>
常用类:
| 类名 | 作用 |
|---|---|
pagination |
分页容器 |
page-item |
分页项目 |
page-link |
分页链接 |
active |
当前页 |
disabled |
禁用状态 |
pagination-lg |
大尺寸分页 |
pagination-sm |
小尺寸分页 |
居中显示分页:
html
<ul class="pagination justify-content-center">
<li class="page-item"><a class="page-link" href="#">1</a></li>
<li class="page-item active"><a class="page-link" href="#">2</a></li>
<li class="page-item"><a class="page-link" href="#">3</a></li>
</ul>
注意:Bootstrap 只负责分页的显示样式。真正的数据分页需要后端或者 JavaScript 实现。
1.11.4 Dropdown(下拉菜单)
Dropdown 是下拉菜单组件,点击按钮后显示菜单选项。
常见用途:
- 用户个人中心。
- 设置菜单。
- 操作菜单。
- 导航栏下拉菜单。
示例:
html
<div class="dropdown">
<!-- 下拉菜单按钮 -->
<button
class="btn btn-primary dropdown-toggle"
type="button"
data-bs-toggle="dropdown"
>
更多操作
</button>
<!-- 下拉菜单 -->
<ul class="dropdown-menu">
<li>
<a class="dropdown-item" href="#">个人资料</a>
</li>
<li>
<a class="dropdown-item" href="#">修改密码</a>
</li>
<li>
<hr class="dropdown-divider">
</li>
<li>
<a class="dropdown-item" href="#">退出登录</a>
</li>
</ul>
</div>
常用类:
| 类名 | 作用 |
|---|---|
dropdown |
下拉菜单容器 |
dropdown-toggle |
下拉菜单按钮样式 |
dropdown-menu |
菜单容器 |
dropdown-item |
菜单选项 |
dropdown-divider |
菜单分隔线 |
dropdown-header |
菜单标题 |
1.11.5 Accordion(折叠面板)
Accordion 用于将多个内容区域折叠起来,点击标题时展开内容。
例如:
- 常见问题 FAQ。
- 系统设置。
- 分类菜单。
- 详细信息展示。
示例:
html
<div class="accordion" id="myAccordion">
<!-- 第一个面板 -->
<div class="accordion-item">
<h2 class="accordion-header">
<button
class="accordion-button"
type="button"
data-bs-toggle="collapse"
data-bs-target="#itemOne"
>
什么是 Bootstrap?
</button>
</h2>
<div
id="itemOne"
class="accordion-collapse collapse show"
data-bs-parent="#myAccordion"
>
<div class="accordion-body">
Bootstrap 是一个前端 CSS 框架。
</div>
</div>
</div>
<!-- 第二个面板 -->
<div class="accordion-item">
<h2 class="accordion-header">
<button
class="accordion-button collapsed"
type="button"
data-bs-toggle="collapse"
data-bs-target="#itemTwo"
>
Bootstrap 有什么作用?
</button>
</h2>
<div
id="itemTwo"
class="accordion-collapse collapse"
data-bs-parent="#myAccordion"
>
<div class="accordion-body">
可以快速构建美观的响应式网页。
</div>
</div>
</div>
</div>
重要知识点:
accordion:折叠面板容器。accordion-item:单个面板。accordion-button:面板标题按钮。accordion-collapse:可折叠内容区域。collapse show:默认展开。collapsed:默认折叠状态。data-bs-parent:指定父级容器,实现同一组面板展开一个时自动收起其他面板。
1.11.6 Spinner(加载动画)
Spinner 用于显示加载状态。
例如:
- 正在加载数据。
- 正在提交表单。
- 正在请求服务器。
- 正在上传文件。
圆形加载动画:
html
<div class="spinner-border text-primary" role="status">
<span class="visually-hidden">
正在加载...
</span>
</div>
不同颜色:
html
<div class="spinner-border text-primary"></div>
<div class="spinner-border text-success"></div>
<div class="spinner-border text-danger"></div>
<div class="spinner-border text-warning"></div>
小尺寸加载动画:
html
<div class="spinner-border spinner-border-sm text-primary"></div>
按钮中的加载动画:
html
<button class="btn btn-primary" disabled>
<span class="spinner-border spinner-border-sm"></span>
正在加载...
</button>
另外还有一种成长型加载动画:
html
<div class="spinner-grow text-primary" role="status">
<span class="visually-hidden">正在加载...</span>
</div>
1.12 Bootstrap 实战
前面学习了 Bootstrap 的基础样式和常用组件,现在通过几个完整案例,把这些知识组合起来。
下面的案例均可以独立保存为 HTML 文件运行。
1.12.1 登录页面
登录页面通常包含:
- 用户名输入框。
- 密码输入框。
- 记住我复选框。
- 登录按钮。
- 注册入口。
示例:
html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>用户登录</title>
<link
href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.8/dist/css/bootstrap.min.css"
rel="stylesheet"
>
</head>
<body class="bg-light">
<!-- container:页面居中 -->
<div class="container">
<!-- vh-100:占满屏幕高度 -->
<!-- d-flex:使用 Flex 布局 -->
<!-- justify-content-center:水平居中 -->
<!-- align-items-center:垂直居中 -->
<div class="vh-100 d-flex justify-content-center align-items-center">
<!-- 登录卡片 -->
<div class="card shadow-sm" style="width: 100%; max-width: 400px;">
<div class="card-body p-4">
<h3 class="text-center mb-4">
用户登录
</h3>
<form>
<!-- 用户名 -->
<div class="mb-3">
<label class="form-label">
用户名
</label>
<input
type="text"
name="username"
class="form-control"
placeholder="请输入用户名"
required
>
</div>
<!-- 密码 -->
<div class="mb-3">
<label class="form-label">
密码
</label>
<input
type="password"
name="password"
class="form-control"
placeholder="请输入密码"
required
>
</div>
<!-- 记住我 -->
<div class="form-check mb-3">
<input
type="checkbox"
class="form-check-input"
id="remember"
>
<label class="form-check-label" for="remember">
记住我
</label>
</div>
<!-- 登录按钮 -->
<button type="submit" class="btn btn-primary w-100">
登录
</button>
</form>
<div class="text-center mt-3">
<span class="text-muted">
还没有账号?
</span>
<a href="#">立即注册</a>
</div>
</div>
</div>
</div>
</div>
</body>
</html>
这个案例用到了:
container:页面容器。vh-100:占满屏幕高度。d-flex:Flex 布局。justify-content-center:水平居中。align-items-center:垂直居中。card:卡片。shadow-sm:小尺寸阴影。form-control:输入框。btn btn-primary:登录按钮。
1.12.2 后台管理首页
后台管理系统通常包含:
- 顶部导航栏。
- 左侧菜单。
- 右侧内容区域。
- 数据统计卡片。
下面制作一个简单的后台管理首页。
html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>后台管理系统</title>
<link
href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.8/dist/css/bootstrap.min.css"
rel="stylesheet"
>
</head>
<body class="bg-light">
<!-- 顶部导航栏 -->
<nav class="navbar navbar-expand-lg bg-dark" data-bs-theme="dark">
<div class="container-fluid">
<a class="navbar-brand" href="#">
管理系统
</a>
<div class="ms-auto">
<a href="#" class="nav-link text-white">
管理员
</a>
</div>
</div>
</nav>
<!-- 页面主体 -->
<div class="container-fluid">
<div class="row">
<!-- 左侧菜单 -->
<aside class="col-md-2 bg-white min-vh-100 p-3">
<h6 class="text-muted mb-3">
系统菜单
</h6>
<div class="nav flex-column">
<a href="#" class="nav-link active">
首页
</a>
<a href="#" class="nav-link text-dark">
用户管理
</a>
<a href="#" class="nav-link text-dark">
文章管理
</a>
<a href="#" class="nav-link text-dark">
系统设置
</a>
</div>
</aside>
<!-- 右侧内容 -->
<main class="col-md-10 p-4">
<h3 class="mb-4">
控制台
</h3>
<!-- 数据统计 -->
<div class="row g-3">
<div class="col-12 col-md-6 col-xl-3">
<div class="card border-0 shadow-sm">
<div class="card-body">
<p class="text-muted mb-2">
用户总数
</p>
<h3>1,280</h3>
</div>
</div>
</div>
<div class="col-12 col-md-6 col-xl-3">
<div class="card border-0 shadow-sm">
<div class="card-body">
<p class="text-muted mb-2">
文章总数
</p>
<h3>356</h3>
</div>
</div>
</div>
<div class="col-12 col-md-6 col-xl-3">
<div class="card border-0 shadow-sm">
<div class="card-body">
<p class="text-muted mb-2">
今日访问
</p>
<h3>2,560</h3>
</div>
</div>
</div>
<div class="col-12 col-md-6 col-xl-3">
<div class="card border-0 shadow-sm">
<div class="card-body">
<p class="text-muted mb-2">
待处理任务
</p>
<h3>12</h3>
</div>
</div>
</div>
</div>
<!-- 最近动态 -->
<div class="card border-0 shadow-sm mt-4">
<div class="card-header bg-white">
最近动态
</div>
<div class="card-body">
<p>用户 Tom 注册了账号。</p>
<p>管理员发布了一篇文章。</p>
<p class="mb-0">系统完成了一次数据备份。</p>
</div>
</div>
</main>
</div>
</div>
</body>
</html>
这里重点学习:
container-fluid:全宽页面布局。col-md-2:左侧菜单占据 2 列。col-md-10:右侧内容占据 10 列。row g-3:栅格布局以及元素间距。card:数据统计卡片。shadow-sm:卡片阴影。border-0:去掉边框。
注意:这个示例是桌面端后台布局的基础版本。实际项目中还需要进一步处理手机端侧边栏的折叠或隐藏。
1.12.3 数据列表页面
数据列表是后台管理系统最常见的页面之一。
例如用户管理页面,需要展示用户信息,并提供搜索、新增、编辑、删除和分页功能。
html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>用户管理</title>
<link
href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.8/dist/css/bootstrap.min.css"
rel="stylesheet"
>
</head>
<body class="bg-light">
<div class="container-fluid p-4">
<!-- 页面标题 -->
<div class="d-flex justify-content-between align-items-center mb-4">
<h3>用户管理</h3>
<button class="btn btn-primary">
新增用户
</button>
</div>
<!-- 搜索区域 -->
<div class="card mb-4">
<div class="card-body">
<form class="row g-3">
<div class="col-md-4">
<input
type="text"
class="form-control"
placeholder="请输入用户名"
>
</div>
<div class="col-md-3">
<select class="form-select">
<option value="">全部状态</option>
<option value="1">正常</option>
<option value="0">禁用</option>
</select>
</div>
<div class="col-md-2">
<button type="submit" class="btn btn-primary w-100">
搜索
</button>
</div>
<div class="col-md-2">
<button type="reset" class="btn btn-secondary w-100">
重置
</button>
</div>
</form>
</div>
</div>
<!-- 数据表格 -->
<div class="card">
<div class="card-body">
<div class="table-responsive">
<table class="table table-hover align-middle">
<thead class="table-light">
<tr>
<th>ID</th>
<th>用户名</th>
<th>邮箱</th>
<th>状态</th>
<th>注册时间</th>
<th>操作</th>
</tr>
</thead>
<tbody>
<tr>
<td>1</td>
<td>Tom</td>
<td>tom@example.com</td>
<td>
<span class="badge bg-success">
正常
</span>
</td>
<td>2026-11-01</td>
<td>
<button class="btn btn-sm btn-primary">
编辑
</button>
<button class="btn btn-sm btn-danger">
删除
</button>
</td>
</tr>
<tr>
<td>2</td>
<td>Jack</td>
<td>jack@example.com</td>
<td>
<span class="badge bg-danger">
禁用
</span>
</td>
<td>2026-11-02</td>
<td>
<button class="btn btn-sm btn-primary">
编辑
</button>
<button class="btn btn-sm btn-danger">
删除
</button>
</td>
</tr>
<tr>
<td>3</td>
<td>Lucy</td>
<td>lucy@example.com</td>
<td>
<span class="badge bg-success">
正常
</span>
</td>
<td>2026-11-03</td>
<td>
<button class="btn btn-sm btn-primary">
编辑
</button>
<button class="btn btn-sm btn-danger">
删除
</button>
</td>
</tr>
</tbody>
</table>
</div>
<!-- 分页 -->
<nav>
<ul class="pagination justify-content-end mb-0">
<li class="page-item disabled">
<a class="page-link" href="#">上一页</a>
</li>
<li class="page-item active">
<a class="page-link" href="#">1</a>
</li>
<li class="page-item">
<a class="page-link" href="#">2</a>
</li>
<li class="page-item">
<a class="page-link" href="#">3</a>
</li>
<li class="page-item">
<a class="page-link" href="#">下一页</a>
</li>
</ul>
</nav>
</div>
</div>
</div>
</body>
</html>
这个页面包含:
- 页面标题。
- 搜索输入框。
- 状态筛选下拉框。
- 搜索和重置按钮。
- 用户数据表格。
- 状态徽章。
- 编辑和删除按钮。
- 分页组件。
实际开发时,数据需要由后端提供,而不是像示例一样写死在 HTML 中。
1.12.4 数据新增与编辑页面
新增和编辑页面通常使用相同的表单布局。
例如用户管理系统:
- 新增用户:填写用户名、邮箱、年龄。
- 编辑用户:显示原有信息,修改后保存。
示例:
html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>新增用户</title>
<link
href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.8/dist/css/bootstrap.min.css"
rel="stylesheet"
>
</head>
<body class="bg-light">
<div class="container py-5">
<div class="row justify-content-center">
<div class="col-12 col-lg-8">
<div class="card shadow-sm">
<!-- 标题 -->
<div class="card-header bg-white">
<h4 class="mb-0">
新增用户
</h4>
</div>
<!-- 表单 -->
<div class="card-body p-4">
<form>
<!-- 用户名 -->
<div class="mb-3">
<label class="form-label">
用户名
</label>
<input
type="text"
name="username"
class="form-control"
placeholder="请输入用户名"
required
>
</div>
<!-- 邮箱 -->
<div class="mb-3">
<label class="form-label">
邮箱
</label>
<input
type="email"
name="email"
class="form-control"
placeholder="请输入邮箱"
required
>
</div>
<!-- 年龄 -->
<div class="mb-3">
<label class="form-label">
年龄
</label>
<input
type="number"
name="age"
class="form-control"
min="1"
max="120"
>
</div>
<!-- 性别 -->
<div class="mb-3">
<label class="form-label">
性别
</label>
<select name="gender" class="form-select">
<option value="">请选择</option>
<option value="male">男</option>
<option value="female">女</option>
</select>
</div>
<!-- 个人简介 -->
<div class="mb-4">
<label class="form-label">
个人简介
</label>
<textarea
name="description"
class="form-control"
rows="4"
placeholder="请输入个人简介"
></textarea>
</div>
<!-- 操作按钮 -->
<div class="d-flex gap-2">
<button
type="submit"
class="btn btn-primary"
>
保存
</button>
<a href="#" class="btn btn-secondary">
返回
</a>
</div>
</form>
</div>
</div>
</div>
</div>
</div>
</body>
</html>
新增和编辑页面的区别:
新增页面:
html
<input
type="text"
name="username"
class="form-control"
placeholder="请输入用户名"
>
编辑页面:
html
<input
type="text"
name="username"
class="form-control"
value="Tom"
>
编辑页面通常通过 value 或 textarea 的内容显示原有数据。
如果使用 Django Templates,可以这样写:
html
<!-- 新增与编辑共用一个模板 -->
<form method="post">
{% csrf_token %}
<div class="mb-3">
<label class="form-label">用户名</label>
<input
type="text"
name="username"
class="form-control"
value="{{ user.username|default:'' }}"
required
>
</div>
<div class="mb-3">
<label class="form-label">邮箱</label>
<input
type="email"
name="email"
class="form-control"
value="{{ user.email|default:'' }}"
required
>
</div>
<button type="submit" class="btn btn-primary">
保存
</button>
</form>
这里:
{``{ user.username }}:获取 Django 后端传递的用户名。default:'':如果没有值,则使用空字符串。{% csrf_token %}:Django 表单提交时使用的 CSRF 防护令牌。method="post":使用 POST 请求提交表单。
实际项目中,还需要根据新增或编辑场景,在 Django 视图中处理数据保存和更新。
1.13 Bootstrap 学习总结
学习完本章,建议重点掌握以下内容:
| 分类 | 必须掌握的类名 |
|---|---|
| 容器 | container、container-fluid |
| 栅格 | row、col、col-md-6 |
| 间距 | m-*、p-*、gap-* |
| 尺寸 | w-*、h-* |
| 文字 | fs-*、fw-bold、text-center |
| 颜色 | text-primary、bg-light |
| 按钮 | btn、btn-primary、btn-sm |
| 导航栏 | navbar、navbar-brand、nav-link |
| 卡片 | card、card-body、card-header |
| 表格 | table、table-striped、table-hover |
| 表单 | form-control、form-select、form-check |
| 模态框 | modal、modal-dialog、modal-content |
| 提示框 | alert |
| 徽章 | badge |
| 分页 | pagination、page-item、page-link |
| 下拉菜单 | dropdown、dropdown-menu |
| 折叠面板 | accordion |
| 加载动画 | spinner-border |
2. Django Template 模板系统
Django Template 是 Django 自带的网页模板系统。它可以将 Python 后端处理好的数据传递给 HTML 页面,再由浏览器展示出来。
前面已经学习了 HTML 和 Bootstrap 5,因此本章不再重复介绍 HTML 标签和 Bootstrap 组件,而是重点学习如何使用 Django 将后端数据与网页结合起来。
2.1 Django Template 简介与环境配置
2.1.1 Django Template 的作用
在传统的网站开发中,网页通常由 HTML、CSS 和 JavaScript 组成,而后端负责处理业务逻辑、查询数据库以及提供数据。
Django Template 的作用就是将后端数据与 HTML 页面结合起来。
例如,我们希望在网页中显示一本书的信息:
- 书名:Python 入门
- 作者:张三
- 价格:39.9 元
如果不使用模板,需要手动拼接 HTML 字符串,比较麻烦。
使用 Django Template 后,可以在 HTML 中编写变量占位符,再由 Django 将真实数据填充进去。
html
<!-- templates/book.html -->
<h1>{{ title }}</h1>
<p>作者:{{ author }}</p>
<p>价格:{{ price }} 元</p>
后端只需要传递数据:
python
def book(request):
context = {
"title": "Python 入门",
"author": "张三",
"price": 39.9,
}
return render(request, "book.html", context)
Django 会将变量替换成对应的数据,最终生成普通 HTML 页面并返回给浏览器。
2.1.2 前后端不分离与前后端分离的区别
Django 常见的开发方式有两种。
前后端不分离:
- 后端使用 Django 处理业务逻辑和数据库。
- 后端使用 Django Template 渲染 HTML 页面。
- 浏览器访问 Django 后端,直接获取完整网页。
前后端分离:
- 后端使用 Django REST Framework 提供 API 接口。
- 前端使用 Flutter、Vue 等技术开发界面。
- 前端通过 HTTP 请求获取 JSON 数据,再自行渲染页面。
两种方式的主要区别在于:网页究竟由后端生成,还是由前端生成。
2.1.3 Django Template 的工作流程
以用户访问一个图书列表页面为例:
- 浏览器向 Django 发送请求。
- Django 根据 URL 找到对应的视图函数。
- 视图函数查询数据库,获取图书数据。
- 视图函数将数据传递给模板。
- Django Template 将数据填充到 HTML 中。
- Django 将生成的 HTML 页面返回给浏览器。
- 浏览器解析并展示网页。
可以简单理解为:
text
浏览器
|
| 请求网页
v
Django URL 路由
|
v
视图函数 views.py
|
| 查询数据库、处理数据
v
Django Template
|
| 将数据填充到 HTML
v
生成完整 HTML
|
v
浏览器展示网页
2.1.4 创建 Django 项目与应用
如果已经有 Django 项目,可以直接在现有项目中学习。
如果需要创建新项目,可以执行:
bash
# 创建 Django 项目
django-admin startproject mysite
# 进入项目目录
cd mysite
# 创建应用
python manage.py startapp book
# 启动开发服务器
python manage.py runserver
项目结构示例:
text
mysite/
│
├── manage.py
│
├── mysite/
│ ├── __init__.py
│ ├── settings.py
│ ├── urls.py
│ ├── asgi.py
│ └── wsgi.py
│
└── book/
├── migrations/
├── __init__.py
├── admin.py
├── apps.py
├── models.py
├── tests.py
└── views.py
2.1.5 配置应用
打开 mysite/settings.py,找到 INSTALLED_APPS,将创建的应用添加进去:
python
INSTALLED_APPS = [
"django.contrib.admin",
"django.contrib.auth",
"django.contrib.contenttypes",
"django.contrib.sessions",
"django.contrib.messages",
"django.contrib.staticfiles",
# 注册自己的应用
"book",
]
2.1.6 配置模板目录
在项目根目录下创建 templates 文件夹:
text
mysite/
│
├── manage.py
├── templates/
│ └── index.html
│
├── mysite/
└── book/
打开 settings.py,找到 TEMPLATES 配置:
python
TEMPLATES = [
{
"BACKEND": "django.template.backends.django.DjangoTemplates",
# 配置项目根目录下的 templates 文件夹
"DIRS": [BASE_DIR / "templates"],
# 允许 Django 自动查找各个应用内部的 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",
],
},
},
]
其中:
BACKEND:指定使用 Django 自带的模板引擎。DIRS:指定额外的模板搜索目录。APP_DIRS:是否自动查找应用内部的templates文件夹。OPTIONS:模板引擎的其他配置。
这里的 BASE_DIR 通常是 Django 项目的根目录。
2.1.7 创建第一个 HTML 模板
在项目根目录的 templates 文件夹中创建 index.html:
html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>Django Template</title>
</head>
<body>
<h1>欢迎学习 Django Template</h1>
<p>这是我的第一个 Django 模板页面。</p>
</body>
</html>
接下来创建视图函数:
python
# book/views.py
from django.shortcuts import render
def index(request):
# 渲染 index.html 模板
return render(request, "index.html")
配置 URL:
python
# book/urls.py
from django.urls import path
from . import views
urlpatterns = [
path("", views.index, name="index"),
]
在项目的 urls.py 中引入应用路由:
python
# mysite/urls.py
from django.contrib import admin
from django.urls import path, include
urlpatterns = [
path("admin/", admin.site.urls),
# 将根路径交给 book 应用处理
path("", include("book.urls")),
]
启动服务器:
bash
python manage.py runserver
浏览器访问:
text
http://127.0.0.1:8000/
如果页面显示了欢迎文字,就说明 Django Template 已经配置成功。
2.2 URL 路由与视图函数
URL 路由负责将用户访问的地址映射到对应的视图函数。
视图函数负责处理请求,并返回响应内容。
2.2.1 Django 请求与响应的基本流程
当浏览器访问:
text
http://127.0.0.1:8000/books/
Django 会根据 URL 配置找到对应的视图函数。
视图函数接收一个 request 参数,表示当前 HTTP 请求对象。
处理完成后,视图函数需要返回一个 HTTP 响应对象。
常见的响应方式包括:
HttpResponse():直接返回文本或 HTML。render():渲染 HTML 模板并返回。redirect():重定向到另一个 URL。
2.2.2 URL 路由配置(urls.py)
使用 path() 定义 URL 路由:
python
from django.urls import path
from . import views
urlpatterns = [
path("books/", views.book_list),
path("about/", views.about),
]
其中:
- 第一个参数:URL 路径。
- 第二个参数:对应的视图函数。
- 第三个参数:可选的路由名称等参数。
例如,访问 /books/ 时,Django 就会执行 views.book_list。
2.2.3 编写视图函数(views.py)
视图函数通常定义在应用的 views.py 中。
python
# book/views.py
from django.http import HttpResponse
def book_list(request):
return HttpResponse("这是图书列表页面")
def about(request):
return HttpResponse("这是关于我们页面")
注意:视图函数的第一个参数通常是 request,不能随意省略。
2.2.4 HttpResponse
HttpResponse 用于创建 HTTP 响应对象,可以直接返回文本或 HTML 内容。
python
from django.http import HttpResponse
def hello(request):
return HttpResponse("Hello Django")
def html_page(request):
html = """
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>测试页面</title>
</head>
<body>
<h1>Hello Django</h1>
<p>这是直接返回的 HTML。</p>
</body>
</html>
"""
return HttpResponse(html)
这种方式适合简单的响应,但如果页面内容很多,直接在 Python 中拼接 HTML 就会非常不方便。
因此,实际开发中通常使用 render()。
2.2.5 render() 渲染模板
render() 是 Django 中非常常用的快捷函数。
基本格式:
python
render(request, template_name, context=None)
参数说明:
request:当前请求对象。template_name:模板文件路径。context:传递给模板的数据,通常是字典。
示例:
python
from django.shortcuts import render
def book_list(request):
context = {
"title": "图书列表",
"message": "欢迎来到图书管理系统",
}
return render(request, "book_list.html", context)
模板文件:
html
<!-- templates/book_list.html -->
<h1>{{ title }}</h1>
<p>{{ message }}</p>
Django 会将 context 中的数据传递给模板。
2.2.6 redirect() 页面重定向
redirect() 用于让浏览器跳转到另一个 URL。
例如,用户提交表单后,希望跳转到图书列表页面:
python
from django.shortcuts import redirect
def submit_success(request):
return redirect("/books/")
也可以通过 URL 名称进行重定向:
python
def submit_success(request):
return redirect("book_list")
第二种方式需要在 URL 配置中为路由设置名称。
使用路由名称可以避免在多个地方重复写 URL 地址。
2.2.7 路由参数(动态 URL)
有时我们需要根据 URL 中的参数访问不同的数据。
例如:
text
/books/1/
/books/2/
/books/3/
可以使用动态路由参数:
python
# book/urls.py
from django.urls import path
from . import views
urlpatterns = [
path("books/<int:book_id>/", views.book_detail),
]
其中:
<int:book_id>:表示接收一个整数参数。book_id:参数名称。
对应的视图函数:
python
# book/views.py
from django.http import HttpResponse
def book_detail(request, book_id):
return HttpResponse(f"当前图书 ID:{book_id}")
访问 /books/3/ 时,视图函数接收到的 book_id 就是整数 3。
常见的路径转换器:
| 转换器 | 作用 |
|---|---|
str |
匹配非空字符串,不包含 / |
int |
匹配整数 |
slug |
匹配字母、数字、连字符和下划线 |
uuid |
匹配 UUID 格式 |
path |
匹配包含 / 的非空字符串 |
例如:
python
urlpatterns = [
path("user/<int:user_id>/", views.user_detail),
path("article/<slug:slug>/", views.article_detail),
]
2.2.8 路由命名(name)
可以为每条路由设置一个名称:
python
urlpatterns = [
path("books/", views.book_list, name="book_list"),
path("books/<int:book_id>/", views.book_detail, name="book_detail"),
]
这样就可以通过路由名称引用 URL,而不必直接写死地址。
例如:
python
return redirect("book_list")
在 HTML 模板中也可以使用:
html
<a href="{% url 'book_list' %}">图书列表</a>
2.2.9 reverse() 与反向解析
reverse() 可以根据路由名称生成对应的 URL。
python
from django.urls import reverse
from django.http import HttpResponse
def test(request):
# 根据路由名称生成 URL
url = reverse("book_list")
return HttpResponse(f"图书列表地址:{url}")
如果路由需要参数:
python
def test(request):
# 生成图书详情页 URL
url = reverse("book_detail", args=[3])
return HttpResponse(url)
如果路由配置如下:
python
path("books/<int:book_id>/", views.book_detail, name="book_detail")
那么生成的地址就是:
text
/books/3/
也可以使用 kwargs:
python
url = reverse("book_detail", kwargs={"book_id": 3})
reverse() 和模板中的 {% url %} 都属于 URL 反向解析,可以让代码不依赖具体的 URL 字符串。
2.3 模板变量与数据传递
模板变量用于在 HTML 中显示后端传递的数据。
2.3.1 模板变量({{ variable }})
Django Template 使用双大括号 {``{ }} 表示变量。
例如:
html
<h1>{{ title }}</h1>
<p>{{ message }}</p>
其中 title 和 message 就是模板变量。
如果视图函数传递的数据中包含这些变量,Django 就会将它们替换成实际内容。
2.3.2 在视图函数中传递 context
context 通常是一个字典,用于向模板传递数据。
python
# views.py
from django.shortcuts import render
def index(request):
context = {
"title": "网站首页",
"username": "张三",
"age": 20,
}
return render(request, "index.html", context)
模板中:
html
<!-- index.html -->
<h1>{{ title }}</h1>
<p>用户名:{{ username }}</p>
<p>年龄:{{ age }}</p>
最终页面会显示:
text
网站首页
用户名:张三
年龄:20
注意:模板变量名称必须与传递的字典键对应。
2.3.3 字典数据的传递与访问
可以将 Python 字典传递给模板:
python
def index(request):
user = {
"name": "张三",
"age": 20,
"city": "北京",
}
return render(request, "index.html", {
"user": user,
})
模板中可以通过点号访问字典中的值:
html
<p>姓名:{{ user.name }}</p>
<p>年龄:{{ user.age }}</p>
<p>城市:{{ user.city }}</p>
这里的 user.name 就相当于 Python 中的:
python
user["name"]
不过 Django Template 的点号查找规则并不完全等同于 Python 字典访问,具体规则会在后面介绍。
2.3.4 列表数据的传递与访问
可以将 Python 列表传递给模板:
python
def index(request):
books = [
"Python 入门",
"Django 开发",
"Flutter 实战",
]
return render(request, "index.html", {
"books": books,
})
模板中可以通过索引访问列表元素:
html
<p>{{ books.0 }}</p>
<p>{{ books.1 }}</p>
<p>{{ books.2 }}</p>
这里的 books.0 表示列表中索引为 0 的元素。
注意:列表索引从 0 开始。
如果希望显示全部图书,通常使用模板的 for 标签,而不是一个个写索引。
html
{% for book in books %}
<p>{{ book }}</p>
{% endfor %}
2.3.5 模型对象的传递与访问
除了字典和列表,还可以将 Django ORM 查询得到的模型对象传递给模板。
例如,定义一个图书模型:
python
# models.py
from django.db import models
class Book(models.Model):
title = models.CharField(max_length=100, verbose_name="书名")
author = models.CharField(max_length=50, verbose_name="作者")
price = models.DecimalField(max_digits=6, decimal_places=2, verbose_name="价格")
def __str__(self):
return self.title
视图函数中查询图书:
python
# views.py
from django.shortcuts import render
from .models import Book
def book_detail(request):
# 根据 ID 查询一条图书记录
book = Book.objects.get(id=1)
return render(request, "book_detail.html", {
"book": book,
})
模板中:
html
<h1>{{ book.title }}</h1>
<p>作者:{{ book.author }}</p>
<p>价格:{{ book.price }}</p>
这里的 book.title、book.author 和 book.price 对应模型对象的字段。
这也是 Django Template 中非常常见的使用方式:后端查询数据库,模板负责展示数据。
注意:上面的 get(id=1) 假设数据库中存在 ID 为 1 的记录。如果记录不存在,会产生 Book.DoesNotExist 异常,实际开发时需要进行异常处理。
2.3.6 模板中的点号查找规则
在 Django Template 中,点号 . 不仅可以访问字典,还可以访问对象属性和列表元素。
常见查找方式:
| 写法 | 含义 |
|---|---|
user.name |
访问字典键或对象属性 |
book.title |
访问模型对象属性 |
books.0 |
访问列表第一个元素 |
user.profile.name |
连续访问嵌套属性 |
Django Template 会按照一定顺序进行查找:
- 字典键查找。
- 对象属性或方法查找。
- 列表索引查找。
例如:
python
def index(request):
data = {
"user": {
"name": "张三",
"age": 20,
},
"books": [
"Python",
"Django",
],
}
return render(request, "index.html", {
"data": data,
})
模板中:
html
<p>姓名:{{ data.user.name }}</p>
<p>年龄:{{ data.user.age }}</p>
<p>第一本书:{{ data.books.0 }}</p>
<p>第二本书:{{ data.books.1 }}</p>
特别注意:
模板中的点号查找不等同于 Python 代码中的普通属性访问。模板还会尝试调用符合条件的无参数方法,例如:
html
{{ books.count }}
如果 books 是支持无参数 count() 方法的对象,模板可能会调用它并显示返回值。
但是,模板变量不能像 Python 代码一样随意执行函数,也不能直接传递方法参数。
2.4 模板标签(Template Tags)
模板变量主要用于显示数据,而模板标签可以控制页面的显示逻辑。
例如:
- 根据条件决定是否显示某段内容。
- 循环展示数据库中的多条记录。
- 生成 URL。
- 处理表单中的 CSRF Token。
Django Template 使用 {% %} 表示模板标签。
2.4.1 标签的基本概念
模板变量和模板标签的区别:
html
<!-- 模板变量:显示数据 -->
<h1>{{ username }}</h1>
<!-- 模板标签:控制模板逻辑 -->
{% if username %}
<p>欢迎回来</p>
{% endif %}
简单理解:
{``{ }}:主要负责显示数据。{% %}:主要负责执行模板逻辑。
2.4.2 if 条件判断
if 用于根据条件决定是否显示 HTML 内容。
html
{% if age >= 18 %}
<p>已经成年</p>
{% endif %}
如果条件成立,就显示对应内容;否则不显示。
2.4.3 elif 与 else
可以使用 elif 和 else 处理多个条件:
html
{% if score >= 90 %}
<p>优秀</p>
{% elif score >= 60 %}
<p>及格</p>
{% else %}
<p>不及格</p>
{% endif %}
注意:每个 if 都需要使用 endif 结束。
2.4.4 for 循环
for 用于遍历列表、QuerySet 等可迭代数据。
视图函数:
python
def book_list(request):
books = [
"Python 入门",
"Django 开发",
"Flutter 实战",
]
return render(request, "book_list.html", {
"books": books,
})
模板:
html
<h1>图书列表</h1>
{% for book in books %}
<p>{{ book }}</p>
{% endfor %}
页面会依次显示列表中的每一本书。
也可以遍历模型对象:
html
{% for book in books %}
<div>
<h3>{{ book.title }}</h3>
<p>作者:{{ book.author }}</p>
<p>价格:{{ book.price }}</p>
</div>
{% endfor %}
2.4.5 forloop 计数器
在 for 循环中,Django 提供了 forloop 变量,可以获取当前循环的状态。
常用属性:
| 属性 | 作用 |
|---|---|
forloop.counter |
当前循环次数,从 1 开始 |
forloop.counter0 |
当前循环索引,从 0 开始 |
forloop.first |
是否为第一次循环 |
forloop.last |
是否为最后一次循环 |
forloop.revcounter |
距离循环结束还剩几次,从 1 开始 |
forloop.revcounter0 |
距离循环结束还剩几次,从 0 开始 |
示例:
html
{% for book in books %}
<p>
{{ forloop.counter }}.
{{ book }}
</p>
{% endfor %}
如果列表中有三本书,显示结果就是:
text
1. Python 入门
2. Django 开发
3. Flutter 实战
也可以判断第一条和最后一条数据:
html
{% for book in books %}
{% if forloop.first %}
<p>第一本图书:{{ book }}</p>
{% endif %}
{% if forloop.last %}
<p>最后一本图书:{{ book }}</p>
{% endif %}
{% endfor %}
2.4.6 empty 空列表处理
empty 用于处理列表为空的情况。
html
{% for book in books %}
<p>{{ book.title }}</p>
{% empty %}
<p>暂时没有图书数据</p>
{% endfor %}
如果 books 中有数据,就执行循环内容;如果列表为空,就显示 empty 后面的内容。
这种写法非常适合数据库列表页面。
2.4.7 with 临时变量
with 可以在模板中创建一个临时变量,方便重复使用某个数据。
html
{% with username=user.name %}
<h1>欢迎,{{ username }}</h1>
<p>当前用户:{{ username }}</p>
{% endwith %}
这里的 username 是在 with 代码块中创建的临时变量。
也可以用来简化较长的模板表达式:
html
{% with author=book.author %}
<p>作者:{{ author }}</p>
{% endwith %}
注意:临时变量通常只在 with 代码块内部有效。
2.4.8 url 路由标签
url 标签可以根据路由名称生成 URL。
例如,路由配置:
python
urlpatterns = [
path("books/", views.book_list, name="book_list"),
path("books/<int:book_id>/", views.book_detail, name="book_detail"),
]
模板中:
html
<!-- 不需要自己手动拼接 URL -->
<a href="{% url 'book_list' %}">图书列表</a>
<!-- 带有动态参数的 URL -->
<a href="{% url 'book_detail' book.id %}">查看详情</a>
也可以使用关键字参数:
html
<a href="{% url 'book_detail' book_id=book.id %}">查看详情</a>
使用 url 标签的好处是:即使以后修改了 URL 路径,只要路由名称不变,模板中的链接通常就不需要修改。
2.4.9 csrf_token 跨站请求伪造保护
CSRF 是 Cross-Site Request Forgery 的缩写,中文通常称为跨站请求伪造。
简单理解:攻击者可能诱导已经登录的网站用户,在不知情的情况下向网站发送恶意请求。
Django 内置了 CSRF 防护机制。
当 HTML 表单使用 POST 方法向 Django 提交数据时,通常需要在表单中添加:
html
<form method="POST">
{% csrf_token %}
<input type="text" name="username">
<button type="submit">提交</button>
</form>
{% csrf_token %} 会生成一个隐藏的表单字段,用于携带 CSRF Token。
Django 会在处理 POST 请求时检查 Token。
2.5 模板过滤器(Template Filters)
模板过滤器用于对模板变量进行简单处理,例如:
- 设置默认值。
- 获取字符串或列表长度。
- 格式化日期。
- 截断过长的文本。
- 转换大小写。
- 拼接列表内容。
Django Template 使用 | 符号表示过滤器。
基本格式:
html
{{ variable|filter }}
也可以连续使用多个过滤器:
html
{{ variable|filter1|filter2 }}
2.5.1 default 默认值
default 用于当变量不存在或值为空时,显示默认内容。
html
<p>用户名:{{ username|default:"游客" }}</p>
如果 username 没有值,就会显示:
text
用户名:游客
例如,视图函数:
python
def index(request):
return render(request, "index.html", {
"username": "",
})
模板就会显示默认值"游客"。
注意:default 会将空字符串、None、False 等情况视为需要使用默认值的情况。
如果只希望变量不存在或为 None 时使用默认值,可以了解 default_if_none。
html
{{ username|default_if_none:"游客" }}
2.5.2 length 获取长度
length 用于获取字符串、列表等数据的长度。
html
<p>用户名长度:{{ username|length }}</p>
<p>图书数量:{{ books|length }}</p>
例如:
python
username = "Django"
books = ["Python", "Django", "Flutter"]
结果:
text
用户名长度:6
图书数量:3
2.5.3 date 日期格式化
date 用于格式化日期和时间。
例如:
html
<p>发布时间:{{ book.created_at|date:"Y-m-d" }}</p>
常用格式符号:
| 格式符号 | 含义 | 示例 |
|---|---|---|
Y |
四位年份 | 2026 |
y |
两位年份 | 26 |
m |
两位月份 | 11 |
n |
不带前导零的月份 | 11 |
d |
两位日期 | 05 |
j |
不带前导零的日期 | 5 |
H |
24 小时制小时 | 18 |
h |
12 小时制小时 | 06 |
i |
分钟 | 30 |
s |
秒 | 45 |
常见用法:
html
<!-- 2026-11-12 -->
{{ created_at|date:"Y-m-d" }}
<!-- 2026年11月12日 -->
{{ created_at|date:"Y年m月d日" }}
<!-- 2026-11-12 18:30:45 -->
{{ created_at|date:"Y-m-d H:i:s" }}
2.5.4 truncatechars 截断字符串
truncatechars 用于限制字符串显示长度。
html
<p>{{ book.description|truncatechars:20 }}</p>
如果描述超过 20 个字符,Django 会自动截断,并在末尾添加省略号。
例如:
text
原内容:这是一本非常适合初学者学习的 Django 入门教程。
显示:这是一本非常适合初学者学习的...
注意:这里的长度包含最后添加的省略号。
2.5.5 upper / lower 大小写转换
upper 用于将字符串转换为大写。
html
{{ username|upper }}
lower 用于将字符串转换为小写。
html
{{ username|lower }}
例如:
python
username = "Django"
结果:
text
upper:DJANGO
lower:django
2.5.6 join 列表拼接
join 用于将列表中的元素拼接成一个字符串。
html
<p>{{ tags|join:", " }}</p>
视图函数:
python
def index(request):
tags = ["Python", "Django", "Bootstrap"]
return render(request, "index.html", {
"tags": tags,
})
页面显示:
text
Python, Django, Bootstrap
其中 ", " 就是拼接时使用的分隔符。
2.5.7 safe 与自动 HTML 转义
Django Template 默认会对用户传入的字符串进行 HTML 转义,以降低 XSS(跨站脚本攻击)风险。
例如:
python
def index(request):
content = "<h1>Hello Django</h1>"
return render(request, "index.html", {
"content": content,
})
模板:
html
{{ content }}
默认情况下,Django 不会把这段字符串当成真正的 HTML 标签执行,而是进行转义,页面会显示类似:
text
<h1>Hello Django</h1>
如果使用 safe:
html
{{ content|safe }}
Django 就会将这段内容作为 HTML 处理,页面会显示一个真正的一级标题:
text
Hello Django
重要注意事项:
safe 会跳过自动 HTML 转义。如果内容来自用户输入或其他不可信来源,可能造成 XSS 安全问题。
因此,不要随意对用户提交的内容使用 safe。只有在确认内容可信、确实需要渲染 HTML 时才考虑使用。
2.6 静态文件管理(Static Files)
静态文件是指网站中不需要通过 Django 动态生成的文件,例如:
- CSS:控制网页样式。
- JavaScript:实现网页交互。
- 图片:网站 Logo、背景图片等。
- 字体:网站使用的字体文件。
- Bootstrap:前端 CSS 和 JavaScript 文件。
Django 提供了静态文件管理机制,可以统一管理这些文件,并在模板中引用它们。
2.6.1 Django static 静态文件目录
在 Django 项目中,通常会将静态文件放在名为 static 的文件夹中。
例如:
text
mysite/
│
├── manage.py
│
├── static/
│ │
│ ├── images/
│ │ └── logo.png
│ │
│ └── bootstrap/
│ ├── css/
│ │ └── bootstrap.min.css
│ │
│ └── js/
│ └── bootstrap.bundle.min.js
│
├── templates/
│ └── index.html
│
├── mysite/
└── book/
这里将静态文件统一放在项目根目录下的 static 文件夹中。
2.6.2 配置 STATIC_URL
打开 settings.py,找到静态文件配置:
python
# 静态文件访问路径的前缀
STATIC_URL = "static/"
STATIC_URL 表示浏览器访问静态文件时使用的 URL 前缀。
例如:
text
/static/css/style.css
/static/images/logo.png
注意:STATIC_URL 是 URL 路径,不是电脑中的实际文件夹路径。
如果使用项目根目录下的 static 文件夹,还需要配置:
python
# 项目根目录下的静态文件目录
STATICFILES_DIRS = [
BASE_DIR / "static",
]
这样 Django 在开发环境中就可以从项目根目录的 static 文件夹查找静态文件。
常见配置:
python
# settings.py
# 静态文件访问前缀
STATIC_URL = "static/"
# 项目级静态文件目录
STATICFILES_DIRS = [
BASE_DIR / "static",
]
# 执行 collectstatic 时收集静态文件的目标目录
STATIC_ROOT = BASE_DIR / "staticfiles"
这三个配置的作用不同:
| 配置 | 作用 |
|---|---|
STATIC_URL |
静态文件的访问 URL 前缀 |
STATICFILES_DIRS |
额外的静态文件查找目录 |
STATIC_ROOT |
收集静态文件时的目标目录 |
注意:STATICFILES_DIRS 和 STATIC_ROOT 不应该指向同一个目录。
2.6.3 Bootstrap 本地文件引入
前面已经学习过 Bootstrap 5 的 CDN 引入和本地文件引入。
在 Django 项目中,同样可以将 Bootstrap 文件放入静态文件目录。
例如:
text
mysite/
│
├── static/
│ └── bootstrap/
│ ├── css/
│ │ └── bootstrap.min.css
│ │
│ └── js/
│ └── bootstrap.bundle.min.js
│
└── templates/
└── index.html
模板中引入:
html
{% load static %}
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Django + Bootstrap</title>
<!-- 引入 Bootstrap CSS -->
<link
rel="stylesheet"
href="{% static 'bootstrap/css/bootstrap.min.css' %}"
>
</head>
<body>
<div class="container mt-5">
<h1 class="text-primary">Django + Bootstrap 5</h1>
<button class="btn btn-primary">
点击按钮
</button>
</div>
<!-- 引入 Bootstrap JavaScript -->
<script src="{% static 'bootstrap/js/bootstrap.bundle.min.js' %}"></script>
</body>
</html>
这里使用的是 bootstrap.bundle.min.js,它已经包含 Bootstrap 所需的 Popper 功能,使用 Dropdown、Popover 等组件时比较方便。
如果前面已经通过 CDN 学习过 Bootstrap,那么这里主要需要掌握的是:将 Bootstrap 文件放进 Django 的静态文件目录,再通过 {% static %} 引入。
2.6.4 图片引入
图片也可以放在静态文件目录中。
例如:
text
static/
└── images/
└── logo.png
在模板中:
html
{% load static %}
<img src="{% static 'images/logo.png' %}" alt="网站 Logo">
如果需要设置图片大小,可以结合 CSS:
html
<img
src="{% static 'images/logo.png' %}"
alt="网站 Logo"
width="150"
>
也可以使用 Bootstrap 的图片样式:
html
<img
src="{% static 'images/logo.png' %}"
class="img-fluid"
alt="网站 Logo"
>
2.6.5 {% load static %}
在使用 {% static %} 标签之前,需要先加载 Django 的静态文件模板标签:
html
{% load static %}
例如:
html
{% load static %}
<link rel="stylesheet" href="{% static 'css/style.css' %}">
<script src="{% static 'js/main.js' %}"></script>
<img src="{% static 'images/logo.png' %}" alt="Logo">
通常将 {% load static %} 放在 HTML 模板的最前面。
注意:{% load static %} 是加载模板标签库,不是加载某个具体的 CSS 或 JavaScript 文件。
2.6.6 {% static %} 标签
{% static %} 用于生成静态文件的 URL。
基本格式:
html
{% static '文件相对路径' %}
常见用法:
html
{% load static %}
<!-- CSS -->
<link rel="stylesheet" href="{% static 'css/style.css' %}">
<!-- JavaScript -->
<script src="{% static 'js/main.js' %}"></script>
<!-- 图片 -->
<img src="{% static 'images/logo.png' %}" alt="Logo">
<!-- 其他文件 -->
<a href="{% static 'files/manual.pdf' %}">下载使用手册</a>
注意:这里的路径是相对于静态文件目录的路径,不需要再写 static/。
2.6.7 开发环境与生产环境的区别
Django 在开发环境和生产环境中处理静态文件的方式有所不同。
开发环境:
通常使用:
python
DEBUG = True
Django 可以通过开发服务器帮助提供静态文件,方便开发和调试。
生产环境:
通常设置:
python
DEBUG = False
生产环境不应该依赖 Django 开发服务器提供静态文件。
通常需要执行:
bash
python manage.py collectstatic
这个命令会将项目中找到的静态文件收集到 STATIC_ROOT 指定的目录中。
例如:
text
mysite/
├── static/
│ ├── css/
│ └── images/
│
└── staticfiles/
├── css/
└── images/
然后由 Nginx、CDN 或其他静态文件服务负责提供这些文件。
2.7 模板继承与复用
在实际的网站开发中,通常会有很多网页。
如果每个页面都重新编写这些 HTML,不仅代码重复,而且以后修改网站布局时,需要修改多个文件。
Django Template 提供了模板继承和模板包含机制,可以让多个页面共享公共内容。
2.7.1 模板继承的作用
模板继承可以让一个 HTML 模板继承另一个 HTML 模板。
通常:
- 父模板:定义网站公共布局。
- 子模板:继承父模板,并填充自己的页面内容。
例如:
text
templates/
│
├── base.html
│
├── index.html
│
├── book_list.html
│
└── book_detail.html
其中:
base.html:网站公共布局。index.html:首页。book_list.html:图书列表页。book_detail.html:图书详情页。
子模板不需要重复编写导航栏、页脚等公共内容,只需要编写自己的部分。
2.7.2 extends 继承标签
extends 用于指定当前模板继承哪个父模板。
基本格式:
html
{% extends "base.html" %}
例如:
html
<!-- templates/index.html -->
{% extends "base.html" %}
这表示 index.html 继承 base.html。
注意:extends 通常应该放在子模板的最前面,不能放在普通 HTML 内容之后。
2.7.3 block 定义内容区域
父模板中可以使用 block 定义允许子模板修改的区域。
基本格式:
html
{% block block_name %}
默认内容
{% endblock %}
例如,创建父模板:
html
<!-- templates/base.html -->
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>
{% block title %}
Django 网站
{% endblock %}
</title>
</head>
<body>
<header>
<h1>我的网站</h1>
</header>
<main>
{% block content %}
{% endblock %}
</main>
<footer>
<p>版权所有 © 2026</p>
</footer>
</body>
</html>
这里定义了两个区域:
title:网页标题区域。content:网页主要内容区域。
子模板可以根据需要填充这些区域。
2.7.4 endblock 结束内容区域
每一个 block 都需要使用 endblock 结束。
例如:
html
{% block content %}
<h1>首页内容</h1>
<p>欢迎访问我的网站。</p>
{% endblock %}
也可以在结束标签中写上区域名称,方便阅读:
html
{% block content %}
<h1>首页内容</h1>
{% endblock content %}
两种写法都可以。
2.7.5 父模板与子模板
下面通过一个完整示例理解模板继承。
父模板:
html
<!-- templates/base.html -->
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>
{% block title %}
我的 Django 网站
{% endblock %}
</title>
</head>
<body>
<nav>
<a href="/">首页</a>
<a href="/books/">图书列表</a>
</nav>
<main>
{% block content %}
{% endblock %}
</main>
<footer>
<p>网站底部信息</p>
</footer>
</body>
</html>
首页子模板:
html
<!-- templates/index.html -->
{% extends "base.html" %}
{% block title %}
网站首页
{% endblock %}
{% block content %}
<h1>欢迎来到我的网站</h1>
<p>这里是首页内容。</p>
{% endblock %}
图书列表子模板:
html
<!-- templates/book_list.html -->
{% extends "base.html" %}
{% block title %}
图书列表
{% endblock %}
{% block content %}
<h1>图书列表</h1>
<p>这里展示所有图书。</p>
{% endblock %}
最终效果:
首页和图书列表页都会拥有相同的导航栏和页脚,但网页标题和主要内容不同。
这样就实现了公共布局复用。
2.8 Django 表单处理
在实际的网站中,用户经常需要通过表单提交数据,例如搜索图书。
Django 可以通过视图函数接收浏览器提交的数据,并进行处理。
2.8.1 HTML form 表单
HTML 的 form 标签用于创建表单。
基本格式:
html
<form action="请求地址" method="请求方式">
<!-- 表单内容 -->
</form>
常用属性:
| 属性 | 作用 |
|---|---|
action |
指定表单提交到哪个 URL |
method |
指定 HTTP 请求方法 |
常见的表单控件:
input:输入框。textarea:多行文本框。select:下拉选择框。button:按钮。
例如,创建一个图书查询表单:
html
<form action="/books/search/" method="GET">
<div>
<label>书名:</label>
<input type="text" name="title">
</div>
<div>
<label>作者:</label>
<input type="text" name="author">
</div>
<button type="submit">提交</button>
</form>
这里最重要的是 name 属性。
html
<input type="text" name="title">
<input type="text" name="author">
提交后,浏览器实际发送的 URL 类似:
http
/books/search/?title=Python&author=张三
把表单中的数据填入了相应的查询参数中
2.8.2 request.method
request.method 用于获取当前 HTTP 请求的方法。
常见返回值:
python
"GET"
"POST"
可以根据不同请求方法执行不同操作:
python
def book_store(request):
if request.method == "GET":
# 用户访问详情页面
return render(request, "books/book_detail.html")
elif request.method == "POST":
# 用户提交添加表单
return HttpResponse("收到表单数据")
2.8.3 request.GET 获取参数
request.GET 用于获取 GET 请求中的查询参数。
例如,浏览器访问:
text
/books/?keyword=Python&page=2
视图函数:
python
def book_search(request):
# 获取 keyword 参数
keyword = request.GET.get("keyword")
# 获取 page 参数
page = request.GET.get("page")
return HttpResponse(
f"搜索关键词:{keyword},页码:{page}"
)
结果:
text
搜索关键词:Python,页码:2
也可以给参数设置默认值:
python
keyword = request.GET.get("keyword", "")
如果 URL 中没有 keyword 参数,就会返回空字符串。
2.9 数据列表分页(Pagination)
2.9.1 分页的作用
假设数据库中有 100 本图书,如果一次性全部展示,页面会很长。
分页就是将数据分成多个页面,例如每页显示 10 条:
- 第 1 页:第 1~10 本
- 第 2 页:第 11~20 本
- 第 3 页:第 21~30 本
2.9.2 视图函数中实现分页
Django 提供了内置的 Paginator 类,可以帮助我们实现分页功能。
python
# views.py
from django.shortcuts import render
from django.core.paginator import Paginator
from .models import Book
def book_list(request):
# 查询所有图书
books = Book.objects.all().order_by("-id")
# 每页显示 10 条数据
paginator = Paginator(books, 10)
# 获取 URL 中的 page 参数,例如 ?page=2
page_number = request.GET.get("page")
# 获取当前页的数据
page_obj = paginator.get_page(page_number)
return render(request, "book_list.html", {
"page_obj": page_obj
})
2.9.3 模板中展示分页数据
html
<!-- book_list.html -->
<!-- 遍历当前页的图书 -->
{% for book in page_obj %}
<p>{{ book.title }} - {{ book.author }}</p>
{% endfor %}
<!-- 分页导航 -->
<div>
{% if page_obj.has_previous %}
<a href="?page=1">首页</a>
<a href="?page={{ page_obj.previous_page_number }}">上一页</a>
{% endif %}
<span>
第 {{ page_obj.number }} 页 /
共 {{ page_obj.paginator.num_pages }} 页
</span>
{% if page_obj.has_next %}
<a href="?page={{ page_obj.next_page_number }}">下一页</a>
<a href="?page={{ page_obj.paginator.num_pages }}">末页</a>
{% endif %}
</div>
2.9.4 分页知识点
| 知识点 | 作用 |
|---|---|
Paginator(对象列表, 每页数量) |
创建分页器 |
paginator.get_page(number) |
获取指定页的数据 |
page_obj |
当前页对象 |
page_obj.object_list |
当前页的数据列表 |
page_obj.number |
当前页码 |
page_obj.paginator.num_pages |
总页数 |
page_obj.has_previous |
是否有上一页 |
page_obj.has_next |
是否有下一页 |
page_obj.previous_page_number |
上一页页码 |
page_obj.next_page_number |
下一页页码 |
2.10 Django Template 学习总结
学习完本章,建议重点掌握以下内容:
| 分类 | 必须掌握的知识点 |
|---|---|
| 模板配置 | TEMPLATES、DIRS、APP_DIRS |
| URL 路由 | path()、include()、name、redirect() |
| 视图函数 | render()、HttpResponse()、request |
| 数据传递 | context、模板变量 {``{ }} |
| 模板标签 | {% if %}、{% for %}、{% empty %}、{% with %} |
| URL 标签 | {% url %}、{% csrf_token %} |
| 模板过滤器 | default、length、date、truncatechars、upper、lower、join |
| 静态文件 | STATIC_URL、STATICFILES_DIRS、STATIC_ROOT、{% load static %}、{% static %} |
| 模板继承 | {% extends %}、{% block %}、{% endblock %} |
| 表单处理 | GET、request.GET、name、csrf_token |
| ORM 查询 | Model.objects.all()、filter()、get()、order_by() |
| 分页 | Paginator、get_page() |