【复古】浅尝Django前后端不分离

DRF复习笔记(2026年10月)

之前我们已经学过用 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 框架,主要提供:

  1. CSS 样式: 按钮、表格、表单、颜色、间距等。
  2. 页面布局: 容器、栅格系统、响应式布局等。
  3. UI 组件: 导航栏、卡片、模态框、分页、下拉菜单等。
  4. 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)通常放在网页顶部,用于展示网站名称、首页、关于我们、登录等入口。

最简单的导航栏:

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>

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)

模态框是一种弹出式窗口,通常用于展示额外信息、确认操作或者填写表单。

例如:

  • 点击登录,弹出登录窗口。
  • 点击删除,弹出确认窗口。
  • 点击新增,弹出数据录入窗口。

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 实现。

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>

这个页面包含:

  1. 页面标题。
  2. 搜索输入框。
  3. 状态筛选下拉框。
  4. 搜索和重置按钮。
  5. 用户数据表格。
  6. 状态徽章。
  7. 编辑和删除按钮。
  8. 分页组件。

实际开发时,数据需要由后端提供,而不是像示例一样写死在 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 的工作流程

以用户访问一个图书列表页面为例:

  1. 浏览器向 Django 发送请求。
  2. Django 根据 URL 找到对应的视图函数。
  3. 视图函数查询数据库,获取图书数据。
  4. 视图函数将数据传递给模板。
  5. Django Template 将数据填充到 HTML 中。
  6. Django 将生成的 HTML 页面返回给浏览器。
  7. 浏览器解析并展示网页。

可以简单理解为:

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 会按照一定顺序进行查找:

  1. 字典键查找。
  2. 对象属性或方法查找。
  3. 列表索引查找。

例如:

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()
相关推荐
Dmz丶1 小时前
前端转 AI 100 天 Day 44:前端集成实战——用 React 给 Agent 做个正式界面
python
jay神1 小时前
基于 YOLOv8 的手势识别系统(Web 版)
前端·人工智能·python·深度学习·yolo·计算机视觉·计算机毕业设计
屏什么录1 小时前
长时间录屏中途崩溃:MP4 打不开的 6 种救回方案与参数设置
前端·音视频开发
DeepVisionary1 小时前
阿里云上线旗舰多模态新模型,不公布跑分且限时14天退役
python·自动化
东莞市云毅网络有限公司1 小时前
FAQ 覆盖度打分:TF-IDF 与向量嵌入双路召回的 Python 实现
python·数据清洗·rag·企业知识库·文档解析
爱笑的k112 小时前
nano-vllm 源码剖析:千行 Python 手写离线推理引擎的调度、显存与并行时序
开发语言·python·vllm
code_Bo2 小时前
不只命令行:我把跨 Agent 会话同步做成了桌面应用
前端·javascript·后端
kyriewen2 小时前
AI 能把页面写出来,为什么改个需求就开始崩?
前端·javascript·人工智能
用户8356290780512 小时前
使用 Python 控制 Excel 工作表的分页位置
后端·python