Skip to main content

9.实现学生管理列表页

前面几节课,我们已经把班级管理增、删、改、查 全部跑通了。如下图:

image-20260104133620874

image-20260104134506645

image-20260104175710142

image-20260104111317217

从本节开始,我们将开始完成学生管理功能。

1.接入 students 模块的 URL

老规矩,任何功能,先从 URL 开始

打开项目的全局路由 urls.py, 我们把学生模块挂进来:

# 代码位置:config/urls.py
from django.contrib import admin
from django.urls import path,include

urlpatterns = [
path('admin/', admin.site.urls),
path('grades/',include('grades.urls')),
# 新增学生管理
path('students/', include('students.urls')),
]

这一行的意义只有一个:

告诉 Django:学生相关的功能,交给 students 应用。

2.创建 students/urls.py

进入 students 应用目录,新建文件:

students/
├── urls.py

目录结果如下图:

image-20260105130726491

在这里,我们先只做一件事: 👉 学生列表页

# 代码位置: students/urls.py
from django.urls import path
from .views import StudentListView

urlpatterns = [
path('', StudentListView.as_view(), name='student_list'),
]

此时目标非常明确:

访问 /students/,就能看到学生列表。

3.创建 StudentListView

接下来进入 students/views.py。这一步我们已经非常属性。

我们继续使用 Django 的通用类视图:

from django.views.generic import ListView
from .models import Student

class StudentListView(ListView):
model = Student
template_name = 'students/students_list.html'
context_object_name = 'students'
paginate_by = 10

这里我只强调三点:

  1. ListView 👉 列表页的标准解法
  2. context_object_name 👉 模板中用 students
  3. paginate_by 👉 后面做分页时直接生效

4.创建学生模型

接下来我们来创建学生模型。我们先来看下添加学生页面效果。

image-20260105131356718

从图片中我们可以看到学生信息内容较多,但是不用慌,和前面创建的班级模型都是类似的。

进入 students/models.py,编写如下代码:

# 代码位置:students/models.py
from django.db import models
from django.contrib.auth.models import User

from grades.models import Grade

# Create your models here.
GENDER_CHOICES = [
('M', '男'),
('F', '女'),
]

class Student(models.Model):
student_number = models.CharField(max_length=20, unique=True,verbose_name='学籍号')
student_name = models.CharField(max_length=50, verbose_name='姓名')
gender = models.CharField(max_length=1, choices=GENDER_CHOICES, verbose_name='性别')
birthday = models.DateField(verbose_name='出生日期', help_text='格式例如:2020-05-01')
contact_number = models.CharField(max_length=20, verbose_name='联系方式')
address = models.TextField(verbose_name='家庭住址')

# user表一对一关联
user = models.OneToOneField(User, on_delete=models.CASCADE) # 设置外键

# 班级表一对多关联
grade = models.ForeignKey(Grade, on_delete=models.CASCADE, related_name='students', verbose_name='班级')

def __str__(self):
return self.student_name

class Meta:
db_table = "student"
verbose_name_plural = "学生信息"
verbose_name = "学生信息"

【代码解析】

  1. gender字段我们希望使用下拉列表形式展示,所以设置为choices=GENDER_CHOICES,而GENDER_CHOICES的值只能是,存储在数据库中时,我们使用MF来代替。

  2. birthday字段是日期类型,使用DateField类。

  3. address字段内容较长,所有使用TextField类。

  4. user字段和Django自带的auth_user表关联,并且一个student学生对应一个auth_user账号。on_delete=models.CASCADE表示级联删除,也就是说当删除auth_user表中数据时,对应的学生信息也会被删除。

说明:

很多小伙伴反馈说级联删除关系总是容易混淆。例如:

user = models.OneToOneField(User, on_delete=models.CASCADE)

这里表示删除auth_user表时,student表数据很被同时删除。我们可以这样理解记忆:

on_delete 决定的是: 「当 我依赖的对象 被删了,我该怎么办」 而不是**「当我被删了,要不要删别人」**

我们可以通过Django shell 来演示一下。

student表数据如下:

image-20260109090321337

auth_user表数据如下: image-20260109090355709

注意:student.user_id == auth_user.id

在shell中导入2个模型:

9f36807635dd556c6d26ff8ff7ccd7e9

删除student表中,id为6的学生信息,也就是学生名为“张亮”,user_id为7的记录。这只会删除1条记录,也就是student表中记录。如下图: bdfe1164e84c951af11d8e1e7388a9a8

删除auth_user表中,id为10的学生信息,对应中student表中id为9,学生名为“张欣”的记录。这样会同时删除2条记录:student表1条,auth_user表1条。如下图: 0981095620ff6f465632215028436c1e

5.执行迁移:让模型真正落库

模型写完,一定要迁移

顺序永远是这两个命令:

python manage.py makemigrations
python manage.py migrate

image-20260105132937983

执行完成后,你可以做一件很重要的事:

👉 打开数据库工具 👉 看一眼 student 表 👉 确认字段是否和你预期一致

这是一个非常专业的习惯

如下图:

image-20260105133027100

当我第一次看到生成student表字段时,心中有这样的疑惑,为什么模型中定义的user字段:

user = models.OneToOneField(User, on_delete=models.CASCADE) # 设置外键

但是生成的数据表中,却变成了user_id字段。

这是Django在ORM中进行了处理,它并不会在数据库中真的创建名叫 user 的字段,而是会自动创建:

user_id。这是一个外键字段(ForeignKey / OneToOneField 本质上都是外键),Django 的命名约定就是:

{字段名}_id

例如user_id =2 , 那么它就对应着auth_user表中id=2的对象。从而实现了关联关系,后面我们会使用到。

6.创建学生列表模板

创建完了view视图,Model模型,那么接下来,我们创建模板文件。

MVT是Django框架的核心主线,我们学习时要牢牢记住这一点。

在templates下创建students文件夹,在其下面创建students_list.html文件。目录结构如下:

templates/
└── students/
└── students_list.html

如下图 所示。

image-20260105134114933

这一页我们先不追求“炫”, 只做三件事:

  1. 能访问
  2. 能显示数据
  3. 为后续弹窗留好结构

模板内容你可以直接参考班级列表,稍作修改即可。

Students_list.html代码如下:

{% extends 'base.html' %}

{% block content %}
<div class="right">
<div class="top">
<div class="tool">
<div class="class-info">
<form method="get" action="/students/">
<span>班级: </span>
<select name="grade">
<option value="" selected="">请选择班级</option>
</select>
<span>姓名/学号:</span>
<input type="text" name="search">
<input type="submit" value="搜索">
</form>
</div>
<div class="actions">
<button type="button" class="add" id="add">新增</button>
</div>
</div>
</div>
<div class="bottom">
<table>
<thead>
<tr>
<th><input type="checkbox" id="select-all"></th>
<th>姓名</th>
<th>班级</th>
<th>学号</th>
<th>性别</th>
<th>生日</th>
<th>联系电话</th>
<!-- <th>地址</th> -->
<th>操作</th>
</tr>
</thead>
<tbody>
{% for student in students %}
<tr>
<td><input type="checkbox" name="student_ids" value="{{ student.pk }}"></td>
<td>{{ student.student_name }}</td>
<td>{{ student.grade }}</td>
<td>{{ student.student_number }}</td>
<td>{{ student.gender }}</td>
<td>{{ student.birthday }}</td>
<td>{{ student.contact_number }}</td>
<td>
<a href="#" class="btn btn-primary btn-sm edit">编辑</a>
<a href="#" class="btn btn-danger btn-sm del">删除</a>
</td>
</tr>
`{% endfor %}`

</tbody>
</table>
<!-- 分页导航 -->
<div class="pagination">
{% if page_obj.has_previous %}
<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>
{% endif %}
</div>
</div>
</div>

{% endblock %}

上面的模版代码与班级管理页面非常类似,主要通过{% for %}标签来遍历所有学生信息。只是现在数据库中还没有学生数据,运行效果如下:

image-20260105135436350

本节小结

表面上,这一节我们好像只是:

  • 建了 URL
  • 写了 Model
  • 配了 ListView
  • 建了模板

但实际上,你已经完成了一个非常关键的流程

Django的MVT。

下一步

下一节,我们正式进入核心:

用 Fetch 实现学生的新增、编辑、删除(无刷新)。

到那一步,你写的已经不是“Django 教程项目”,而是真正的现代 Web 管理系统

小伙伴们,下节再见 👋