5.让班级列表页面真正成为页面
我们已经完成了班级管理的列表页:
- 能访问
- 能从数据库读数据
- 页面不会报错
从功能角度来说,这一页已经是合格的。
我猜你心中可能会有这样的疑问:
“功能虽然已经能跑了,但页面这么丑,正常吗?”

如果你现在正有这种感觉,先别急。我可以很负责任地告诉你一句话:
非常正常,而且这是每一个 Web 开发者都会经历的阶段。
你现在处在的,正是从「能用」走向「像产品」的那一步。
那我们这一节到底要解决什么问题?
先别急着写代码,我们先明确目标。
这一节我们只做一件事:
让班级列表页面,具备一个真实管理系统该有的页面结构。
具体来说,我们要解决 3 个新手最容易卡住的问题:
- 如何来美化页面?
- 多个页面重复的结构,难道要每个都写一遍?
- 表格里的数据,怎么从“写死”变成“跟着数据库变”?
只要这三个问题你能走通, 后面的老师管理、学生管理、成绩管理,都会顺理成章。
第一步:别急着改逻辑,先把“静态页面”跑起来
通常我们说的Web开发包括前端+后端。在我们的项目中,Django输入后端开发语言。而前端开发这里我们使用最传统的三剑客:HTML+CSS+JavaScript.
想要把前面的班级列表页美化成下面的样式,那么就需要CSS(层叠样式表)。

本套教程我们重点介绍的是后端Django框架,所以前端页面布局和样式就不带领大家一步一步来编写。这里为大家准备了本项目的前端静态页,如下图。

所谓静态页,是指网页内容是固定不变的。你可以直接用浏览器打开静态页中的grades_list.html文件,查看班级列表的页面效果。
接下来,我们直接把静态页grades_list.html中代码,全部粘贴到项目文件中 grades_list.html。如下图。

这一步你不需要理解每一行代码, 你只需要做一件事:刷新页面,看效果。
如果你发现:
- 结构出来了
- 但样式完全不对
别慌,这一步几乎 100% 都会这样。

为什么页面“有结构却没样式”?
我当年第一次看到这种情况时,第一反应是:
“是不是我模板写错了?”
其实根本不是。
真正的原因只有一个:
CSS、JS、图片这些静态资源,还没有被 Django 正确加载。
第二步:把静态资源“安顿好”
在 Django 项目里,有一个非常明确的约定:
所有静态资源,都放在
static目录下。
所以我们先什么都不想,按规矩来。
创建静态资源目录结构
static/
├── css/
├── js/
├── images/
└── fonts/
然后把对应的文件全部拷贝进去。如下图。

第三步:用 Django 的方式加载静态资源
接下来,打开templates/grades/grades_list.html文件, 发现如下CSS文件。
<link rel="stylesheet" href="static/css/base.css">
<link rel="stylesheet" href="static/css/index.css">
<link rel="stylesheet" href="static/css/iconfont.css">
<link rel="stylesheet" href="static/css/sweetalert2.css">
<script src="static/js/sweetalert2.js" ></script>
按照如下步骤进行修改:
1️⃣ 在模板顶部加载 static
`{% load static %}`
2️⃣ 使用 static 标签
`{% load static %}`
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>大熊课堂校园管理系统</title>
<link rel="stylesheet" href="`{% static 'css/base.css' %}`">
<link rel="stylesheet" href="{% static 'css/index.css' %}">
<link rel="stylesheet" href="{% static 'css/iconfont.css' %}">
<link rel="stylesheet" href="{% static 'css/sweetalert2.css' %}">
<script src="{% static 'js/sweetalert2.js' %}"></script>
</head>
【代码解析】
-
{% load static %}: 加载 staticfiles 应用提供的 static 模板标签,这样后续才能使用{% static ... %}来正确生成静态资源的 URL。 -
{% static 'css/base.css' %}等价于 “/static/css/base.css”.
你现在可能还体会不到差别, 但等你将来部署项目、换环境、上服务器时——这一点,会帮你省掉大量无意义的坑。
【注意】检查settings.py文件中是否配置了STATICFILED_DIRS, 如下图。

一切都准备就绪,请刷新页面,检查页面是否和静态页效果一样。如果依然没有显示样式,那么很可能是因为浏览器有缓存,清除浏览器历史记录重新尝试。
设置页面结构
现在我们来看页面结构。
你会发现:
- 班级管理
- 老师管理
- 学生管理
- 成绩管理
左侧边栏永远不变。

这时候你应该在心里冒出一个问题:
“难道我要在每个页面里都复制一遍侧边栏吗?”
如果你这么做了,未来一定会后悔。
第四步:把“不会变的部分”变成父模板
我的经验是:
只要一个东西在多个页面出现,它就不应该写多次。
创建父模板 base.html
templates/
└── base.html
└── grades
└── grades_list.html
在这个文件中放入:
- HTML 基本结构
- CSS / JS 引入
- 左侧边栏
而中间会变化的部分,用一个占位:
{% block content %}
{% endblock %}
base.html完整代码如下:
`{% load static %}`
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>大熊课堂校园管理系统</title>
<link rel="stylesheet" href="`{% static 'css/base.css' %}`">
<link rel="stylesheet" href="{% static 'css/index.css' %}">
<link rel="stylesheet" href="{% static 'css/iconfont.css' %}">
<link rel="stylesheet" href="{% static 'css/sweetalert2.css' %}">
<script src="{% static 'js/sweetalert2.js' %}"></script>
</head>
<body>
<div class="wrapper">
<div class="left">
<div class="head">
<a href="/students/">
<h2>学生管理系统</h2>
</a>
</div>
<div class="profile">
欢迎 [ admin ]
管理员
</div>
<div class="content">
<ul>
<li class="active">
<span class="iconfont icon-fangzi"></span>
<a href="/grades/" class="nav-link">班级管理</a>
</li>
<li>
<span class="iconfont icon-kapian"></span>
<a href="/teachers/">老师管理</a>
</li>
<li>
<span class="iconfont icon-ren"></span>
<a href="/students/">学生管理</a>
</li>
<li>
<span class="iconfont icon-shuben"></span>
<a href="/scores/">成绩管理</a>
</li>
<li>
<span class="iconfont icon-bianji"></span>
<a href="/change_password/">修改密码</a>
</li>
<li>
<span class="iconfont icon-shangchuan"></span>
<a href="/logout/">退出登录</a>
</li>
</ul>
</div>
</div>
<!-- 左侧结束 -->
{% block content %}
{% endblock %}
</div>
</body>
</html>
第五步:让班级列表页面“站在父模板肩膀上”
回到 grades_list.html,你只需要做两件事:
1️⃣ 继承父模板
{% extends 'base.html' %}
2️⃣ 填充自己的内容
{% block content %}
<!-- 班级列表相关内容 -->
{% endblock %}
刷新页面。
如果效果和之前一模一样,那说明你做对了。
页面没变,但结构已经彻底升级。
最后一步:把“写死的表格”交给数据库
现在再看表格内容。
如果你看到的是:
- 固定的班级名称
- 固定的班级编号
那说明它还不是“真正的管理系统”。
使用 for 循环遍历数据
`{% for grade in grades %}`
<tr>
<td>{{ grade.grade_name }}</td>
<td>{{ grade.grade_number }}</td>
<td>
<a href="#">编辑</a>
<a href="#">删除</a>
</td>
</tr>
`{% endfor %}`
这里的 grades,并不是凭空出现的。
它正是我们在 ListView 中配置的:
context_object_name = 'grades'
你不需要关心 SQL, 也不需要手动查询。
数据变,页面就跟着变。

grades_list.html完整代码如下:
{% extends 'base.html' %}
{% block content %}
<div class="right">
<div class="top">
<div class="tool">
<div class="class-info">
<form method="get" action="/grades/">
<span>班级名称:</span>
<input type="text" name="search" placeholder="搜索班级名称..." value="">
<input type="submit" value="搜索">
<a href="#">
<button type="button" class="add">新增</button>
</a>
</form>
</div>
</div>
</div>
<div class="bottom">
<table>
<thead>
<tr>
<th>班级名称</th>
<th>班级编号</th>
<th>操作</th>
</tr>
</thead>
<tbody>
`{% for grade in grades %}`
<tr>
<td>{{ grade.grade_name }}</td>
<td>{{ grade.grade_number }}</td>
<td>
<a href="#">
<button class="add">编辑</button>
</a>
<a href="#">
<button class="del">删除</button>
</a>
</td>
</tr>
`{% endfor %}`
</tbody>
</table>
<!-- 分页导航 -->
<div class="pagination">
<span class="step-links">
<span class="current">
1 / 1
</span>
</span>
</div>
</div>
</div>
{% endblock %}
小结
如果你完整走通了这一节,其实已经说明一件事:
- 你理解了模板继承
- 你掌握了静态资源管理
- 你已经在用“工程化思维”写 Django
下一节,我们会在这个基础上继续推进:
- 搜索功能是如何实现的
- 分页是如何接进来的
- 一个完整 CRUD 是如何闭环的
你现在做的,已经不是“学 Django”, 而是在一步步搭一个真正的 Web 应用。
但是请别忘记:动手写代码(Get Your Hands Dirty)
【大熊课堂精品课程】
Python零基础入门动画课: https://www.bilibili.com/cheese/play/ss7988
Django+Vue:全栈开发: https://www.bilibili.com/cheese/play/ss8134
PyQT6开发桌面软件: https://www.bilibili.com/cheese/play/ss12314
Python办公自动化: https://www.bilibili.com/cheese/play/ss14990
Cursor AI编程+MCP:零基础实战项目课: https://www.bilibili.com/cheese/play/ss105194189
Pandas数据分析实战: https://www.bilibili.com/cheese/play/ss734522035
AI大模型+Python小白应用实战: https://www.bilibili.com/cheese/play/ss3844