15.将学生信息写入到数据库
表单验证都过了,数据到底是怎么真正落库的?
数据提交成功 ≠ 业务逻辑完成
这一节,才是真正的分水岭。
这一节开始之前,我们必须先把逻辑想清楚,否则代码一定会写乱。
我们现在是在做什么?
👉 新增学生
那学生的数据要存在哪?
- 显然:
student表
但事情并没有这么简单。
1.为什么一定要操作 auth_user 表?
如果你打开 Student 模型,会发现一个非常关键的字段:
Student 是关联 User 的
也就是说:
- 学生 ≠ 只能存在于 student 表
- 学生 本质上也是一个“系统用户”
为什么要这么设计呢?
因为在系统中,一共有3个用户角色:管理员,老师,学生。如果分别设计用户管理,会非常繁琐且冗余。所以我们可以借助Django自身的auth_user表,此外还可以利用Django登录、授权等方法,而不再需要自己去重复造轮子,将更多的时间和精力聚焦于业务逻辑上。
2.新手最容易犯的一个错误
很多人第一反应是:
“那我直接用学生姓名当用户名不就行了?”
听起来很合理,但这是auth_user.username 必须唯一。
现实世界里:
梓轩、梓涵、梓晴不止一个。
所以我们不能使用username, 但是我们可以student_number,因为一个班级里,甚至是学校里,学籍号是唯一的。
3.一个简单又稳妥的设计方案
我当时的设计是这样的(非常推荐你用):
username = 学生姓名 + "_" + 学籍号password = 学籍号后 6 位
这样做有三个好处:
- username 永远唯一
- 管理员好记
- 学生第一次登录成本极低
而且你完全不用担心密码加密问题:
Django 的
create_user会自动帮你加密
你只管传明文。
4.真正开始写代码之前,先理一遍执行顺序
当表单提交后,流程是这样的:
flowchart TB
Start[Form 提交流程]
Start --> A[Form 表单校验]
A -->|失败| B[返回校验错误<br/>JSON]
A -->|通过| C[form_valid]
C --> D[处理 user 表]
D --> E[处理 student 表]
E --> F[返回成功 JSON]
注意一句非常关键的话:
能走到
form_valid,说明数据已经是“干净的”。
5.在 form_valid 中做三件事
第一步:拿到已经验证过的数据
这里必须用:
form.cleaned_data.get(...)
因为:
- 它只包含校验通过的数据
- 绝不会是脏数据
我们需要的只有两个字段:
# 代码位置:students/views.py
def form_valid(self, form):
# 接收字段
student_name = form.cleaned_data.get('student_name')
student_number = form.cleaned_data.get('student_number')
2️⃣处理 auth_user 表(重点)
逻辑非常清晰:
- 拼接 username
- 生成 password(学号后六位)
- 查询 user 是否已存在
- 存在 → 直接用
- 不存在 → 创建
这里一定要用:
# 代码位置:students/views.py
from django.contrib.auth.models import User
def form_valid(self, form):
# 接收字段
student_name = form.cleaned_data.get('student_name')
student_number = form.cleaned_data.get('student_number')
# 拼接用户名和密码
username = student_name + '_' + student_number
password = student_number[-6:]
# 为auth_user表创建一个新记录
user = User.objects.create_user(username=username, password=password)
【代码解析】
- User 是 Django 内置的认证模型(django.contrib.auth.models.User)。
- create_user() 是 Django 专门为创建用户提供的便捷方法,它会:
- 自动对密码进行哈希加密(不能直接用 User.objects.create(),因为密码不会加密)。
- 设置 is_active=True(默认激活)。
- 其他字段(如 email、first_name 等)如果没传则为空。
- 结果:数据库的 auth_user 表里多了一条记录,这个 user 对象就是新创建的用户实例。
3️⃣把 user 绑定到 student 上
这一步非常优雅:
# 代码位置:students/views.py
from django.contrib.auth.models import User
def form_valid(self, form):
# 接收字段
student_name = form.cleaned_data.get('student_name')
student_number = form.cleaned_data.get('student_number')
# 拼接用户名和密码
username = student_name + '_' + student_number
password = student_number[-6:]
# 为auth_user表创建一个新记录
user = User.objects.create_user(username=username, password=password)
# user对象赋值给form
form.instance.user = user
form.save()
【代码解析】
- form 是 StudentForm(继承自 ModelForm),它的 instance 属性就是即将要保存的 Student 模型实例。
- 这是一个一对一外键,表示每个学生对应一个唯一的 Django 用户账号。这里把刚刚创建的 user 赋值给 Student 的 user 字段,相当于告诉 Django:“这个学生要关联这个新用户”。
- 把表单中填写的所有字段(student_name、student_number、grade 等)保存到数据库的 student 表。因为 form.instance.user 已经被赋值,所以 user_id 字段会自动保存刚刚创建的 user 的 ID。
一句话总结:
能让 Django 帮你干的事,别自己写。
完整代码如下:
# 代码位置:students/views.py
class StudentCreateView(BasestudentView, CreateView):
def form_valid(self, form):
# 接收字段
student_name = form.cleaned_data.get('student_name')
student_number = form.cleaned_data.get('student_number')
# 写入到auth_user表
username = student_name + '_' + student_number
password = student_number[-6:]
# 为auth_user表创建一个新记录
user, created = User.objects.get_or_create(
username=username
)
//
if created:
user.set_password(password)
user.save()
form.instance.user = user
form.save()
# 返回json响应
return JsonResponse({
'status': 'success',
'messages': '操作成功'
}, status=200)
【代码解析】
-
只有当 created == True(也就是这次真的新建了用户)时,才会去设置密码
-
set_password():这是 Django 官方推荐的设置密码方法,它会自动对明文密码进行哈希加密(使用 PBKDF2 + salt 等)
-
最后 save() 把修改后的用户保存回数据库。
上面的代码进行了优化,替换了原来的create_user。为什么不直接用 create_user()?
因为 get_or_create() 本身是查找或创建,而 create_user() 是强制创建(如果用户名已存在会抛 IntegrityError)。
这段代码想要达到的效果是:
“如果用户已存在,就什么都不改(尤其是不改密码);只有新建时才设置密码”
6.查看提交结果
点击添加按钮,填写正常的学生信息,点击保存按钮,效果如下图所示。

使用navicat查看数据库中的student表,发现新增了一条记录,如下图:

student表中新增记录的字段的值就是我们表单中填写的学生信息。特别注意user_id字段,这是一个外键,它的2。
继续查看auth_user表,发现新增了一条记录,它的id就是2,如下图:

恭喜你,你已经成功地创建了一条学生记录,并且在auth_user表中,同步创建了一条用户 记录。它们是一对一关系:
student.user_id <---> auth_user.id
7.刷新父页面
虽然我们已经成功地将数据存入到了数据库,但是你也会发现一个问题:弹唱一直在显示,而没有关闭。
这是因为:
- 当前页面是在 iframe 里
- 它是学生列表页的“子页面”
所以:
你刷新的是 iframe,不是主页面。
这一步很多人第一次看到都会“恍然大悟”。
解决方案只有一句 JS:
window.parent.location.reload()
意思是:
- 回到父窗口
- 重新加载列表页
- 新数据自然就出来了
在student_form.html代码中,找到提交成功的代码进行修改,代码如下:
if (data.status === 'success') {
Swal.fire({
icon: 'success',
title: data.message || '提交成功',
text: '学生信息已成功保存',
timer: 2000,
showConfirmButton: false
});
// 新增代码:加载父页面
window.parent.location.reload()
}
但是上面代码有一个问题,添加成功后,没有等待timer设置的2000毫秒,而是直接执行了加载父页面,可以这样修改:
if (data.status === 'success') {
Swal.fire({
icon: 'success',
title: data.message || '提交成功',
text: '学生信息已成功保存',
timer: 2000,
showConfirmButton: false
}).then((result) => {
// timer结束后,再执行刷新父页面
window.parent.location.reload();
});
再添加一个学生信息,查看添加成功后,是否会自动刷新父页面,显示加载后的列表页信息。


本章小结
到这里为止,你已经真正做完了:
- ✔ 表单校验
- ✔ 业务处理
- ✔ user + student 双表写入
- ✔ 前端友好提示
- ✔ 页面自动刷新
这已经是一个完整的“真实系统新增流程”了。
下一步你现在就可以做的事情(强烈建议)
不要急着往下看课程,我建议你现在立刻:
- 再新增 2 个学生
- 故意制造一次错误
- 看 user 表和 student 表是否一一对应
下一节,我们会顺势做一件非常重要的事情:
把“新增”这套逻辑,原封不动地复用到“编辑学生”中
到那一步,你会真正理解:
Django 为什么这么适合做管理系统。
我们下节见 👋
【大熊课堂精品课程】
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