项目长啥样:三种角色,一个系统
- 所有权限
- 建老师账号
- 分配权限组
- 登录 Django 后台
- 录入学生
- 录入成绩
- 登录前台页面
- 查自己的成绩
建项目 + 应用
# 1. 建项目根目录
$ cd /home/django
$ django-admin startproject student_system
$ cd student_system
# 2. 在项目里建一个 user 应用:管学生 / 老师 / 成绩三类数据
$ python3 manage.py startapp user
注册应用 + 改基础配置
修改 student_system/settings.py:
# ① 加 user 应用
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'user', # ← 新加
]
# ② 课堂演示:允许任何 host 访问
ALLOWED_HOSTS = ['*']
# ③ 中文 + 北京时间,看着舒服
LANGUAGE_CODE = 'zh-Hans'
TIME_ZONE = 'Asia/Shanghai'
USE_TZ = False # 关闭时区,时间存什么显示什么
三张表:学生 / 老师 / 成绩
auth_user 表存"账号 + 密码 + 是否员工"。我们再建 student / teacher 两张表存"学号 / 班级 / 邮箱"等业务字段——两张业务表通过 OneToOneField 连到 auth_user。这样:登录认证用 Django 自带的(密码哈希、登录拦截全免费),业务字段我们自己存。
修改 user/models.py:
from django.db import models
from django.contrib.auth.models import User
# =========== 学生表 ===========
# 一个学生 ↔ 一个 auth_user 账号(一对一)
class Student(models.Model):
# OneToOneField:建立一对一关系
# 学生删除时,对应的 User 也一起删(CASCADE)
user = models.OneToOneField(User, on_delete=models.CASCADE,
verbose_name='登录账号')
sno = models.CharField('学号', max_length=20, unique=True)
name = models.CharField('姓名', max_length=30)
klass = models.CharField('班级', max_length=30)
class Meta:
verbose_name = '学生'
verbose_name_plural = '学生' # 后台不显示"Students"
def __str__(self):
return f"{self.sno} {self.name}"
# =========== 老师表 ===========
class Teacher(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE,
verbose_name='登录账号')
name = models.CharField('姓名', max_length=30)
subject = models.CharField('科目', max_length=30)
class Meta:
verbose_name = verbose_name_plural = '老师'
def __str__(self):
return self.name
# =========== 成绩表 ===========
# 一名学生有多次成绩 → 学生是"一",成绩是"多" → ForeignKey 在成绩表
class Score(models.Model):
student = models.ForeignKey(Student, on_delete=models.CASCADE,
verbose_name='学生')
subject = models.CharField('科目', max_length=30)
score = models.DecimalField('分数', max_digits=5, decimal_places=1)
exam_date = models.DateField('考试日期')
class Meta:
verbose_name = verbose_name_plural = '成绩'
def __str__(self):
return f"{self.student.name} {self.subject} {self.score}"
同步到数据库
$ python3 manage.py makemigrations
$ python3 manage.py migrate
'学号')是 verbose_name——后台界面里显示成中文。没设的话英文字段名直接当标题,丑且不友好。
把模型挂到 Django 自带后台
修改 user/admin.py,用 ModelAdmin 子类给后台加点料——这是第9章学过的进阶用法:
from django.contrib import admin
from .models import Student, Teacher, Score
# =========== 学生后台 ===========
@admin.register(Student)
class StudentAdmin(admin.ModelAdmin):
# list_display:列表页显示哪些列
list_display = ('sno', 'name', 'klass', 'user')
# list_filter:右侧筛选侧栏,按班级筛选
list_filter = ('klass',)
# search_fields:上方搜索框,按学号 / 姓名搜
search_fields = ('sno', 'name')
# =========== 老师后台 ===========
@admin.register(Teacher)
class TeacherAdmin(admin.ModelAdmin):
list_display = ('name', 'subject', 'user')
list_filter = ('subject',)
# =========== 成绩后台 ===========
@admin.register(Score)
class ScoreAdmin(admin.ModelAdmin):
list_display = ('student', 'subject', 'score', 'exam_date')
list_filter = ('subject', 'exam_date')
# date_hierarchy:上方按日期分级筛选(年/月/日下拉)
date_hierarchy = 'exam_date'
| 属性 | 作用 |
|---|---|
list_display | 列表页要显示的字段 |
list_filter | 右侧侧栏筛选项 |
search_fields | 上方搜索框搜哪几个字段 |
date_hierarchy | 按日期字段分级钻取 |
建超级管理员 + 启动
$ python3 manage.py createsuperuser
# 用户名 admin / 邮箱随便填 / 密码自定(输入时不显示是正常的)
$ python3 manage.py runserver 0.0.0.0:8000
访问 http://IP:8000/admin/,用 admin 登录——能看到学生 / 老师 / 成绩三栏,所有增删改查界面 Django 已经替你画好了。
给"老师"角色定一组权限
- 权限(Permission):每个模型自动生成 4 个,比如
user.add_student、user.change_student、user.delete_student、user.view_student。 - 分组(Group):一组权限的集合。比如建一个"老师组",包含"管学生 + 管成绩"。
- 用户(User):被归到哪个组,就拥有那个组的全部权限。
步骤:在 admin 后台手动操作
- 用 admin 登录
/admin/ - 左侧找 认证和授权 → 组,点"增加组"
- 组名填 老师
- 下方"权限"列表里挑选 → 选
user | 学生 | Can add 学生/Can change 学生/Can view 学生/Can change 学生,再选user | 成绩 | ...一组(不要给老师 delete 学生权限) - 保存
建一个老师账号
- 左侧 认证和授权 → 用户 → "增加用户"
- 用户名 teacher1,密码 teacher123,保存
- 下一页:勾选 is_staff(员工状态)= True——这样他能登录后台
- 下方"组"里把刚建的"老师"组加进去
- 保存
🎉 验收:用 teacher1 登录
退出 admin → 用 teacher1 / teacher123 登录 /admin/
会看到只有"学生"和"成绩"两栏,不能操作老师 / 用户 / 组——权限自动起作用
而且学生那栏也只能"添加 / 修改",不能删除(因为我们没给 delete 权限)
不要给某个用户单独配权限——以后他离职、换岗、改职责时,逐个改用户配置就崩了。给"角色"配权限,把人放进角色,一改改一片。
学生查成绩页:不用后台,自己写一个
学生不该看到管理后台——他们要的是一个简单的"登录 → 看自己的成绩"页面。
① 路由
修改 student_system/urls.py:
from django.contrib import admin
from django.urls import path
from user import views
urlpatterns = [
path('admin/', admin.site.urls),
path('', views.index), # 首页 = 学生登录页
path('login/', views.student_login), # 学生登录
path('scores/', views.my_scores), # 我的成绩
path('logout/', views.student_logout), # 退出
]
② 视图
修改 user/views.py:
from django.shortcuts import render, redirect
from django.contrib.auth import authenticate, login, logout
from .models import Student, Score
def index(request):
return redirect('/login/')
def student_login(request):
if request.method == 'POST':
sno = request.POST.get('sno')
password = request.POST.get('password')
# 关键设计:学号当 username 用
# authenticate 是 Django 自带的密码校验函数
# 它内部会处理"哈希后比较"——比自己写的安全得多
user = authenticate(request, username=sno, password=password)
if user is not None:
login(request, user) # Django 帮你写 session
return redirect('/scores/')
else:
return render(request, 'login.html',
{'error': '学号或密码错误'})
return render(request, 'login.html')
def my_scores(request):
# request.user 是当前登录的 User 对象
# 没登录时 request.user.is_authenticated 是 False
if not request.user.is_authenticated:
return redirect('/login/')
# 通过 user 反查 Student(OneToOne 反向访问)
try:
student = request.user.student # ↑ 注意小写
except Student.DoesNotExist:
return render(request, 'login.html',
{'error': '此账号不是学生账号'})
# 这个学生的所有成绩
scores = Score.objects.filter(student=student).order_by('-exam_date')
return render(request, 'scores.html',
{'student': student, 'scores': scores})
def student_logout(request):
logout(request)
return redirect('/login/')
authenticate + login:
- 密码自动哈希比较(不存明文)
- 登录态自动写入 session(处理后续请求时通过
request.user直接拿到) - 支持"记住我"、密码重置、邮箱激活等扩展
③ 两个模板
新建 user/templates/login.html(在 user 应用里建 templates 目录):
<!DOCTYPE html>
<html>
<head><meta charset="UTF-8"><title>智慧星 · 学生登录</title></head>
<body style="font-family:sans-serif; max-width:360px; margin:80px auto;">
<h2>🎓 智慧星 · 学生登录</h2>
<form method="post">
{% csrf_token %}
<input name="sno" placeholder="学号"
style="width:100%; padding:8px; margin:5px 0;">
<input name="password" type="password" placeholder="密码"
style="width:100%; padding:8px; margin:5px 0;">
<button style="width:100%; padding:10px; background:#3B6B9A; color:#fff; border:none;">登录</button>
</form>
{% if error %}<p style="color:#B83B2E">❌ {{ error }}</p>{% endif %}
</body>
</html>
新建 user/templates/scores.html:
<!DOCTYPE html>
<html>
<head><meta charset="UTF-8"><title>我的成绩</title></head>
<body style="font-family:sans-serif; max-width:720px; margin:30px auto;">
<h2>👋 {{ student.name }} ({{ student.sno }}) 的成绩</h2>
<p>班级:{{ student.klass }} ·
<a href="/logout/">退出</a></p>
<table border="1" cellpadding="6"
style="border-collapse:collapse; width:100%;">
<tr><th>科目</th><th>成绩</th><th>考试日期</th></tr>
{% for s in scores %}
<tr>
<td>{{ s.subject }}</td>
<td>{{ s.score }}</td>
<td>{{ s.exam_date }}</td>
</tr>
{% empty %}
<tr><td colspan="3">暂无成绩</td></tr>
{% endfor %}
</table>
</body>
</html>
④ 建测试学生 + 跑通
注意:学生账号要在 admin 后台建——先建一个 User(用户名 = 学号,比如 2026001,密码自定),再回到 user → 学生 里建一条 Student 关联到这个 User。
然后浏览器访问 http://IP:8000/login/,用学号 + 密码登录,看到自己的成绩列表。
📋 本节小结
项目模块归属
| 谁用 | 访问入口 | 怎么实现 |
|---|---|---|
| 管理员 / 老师 | /admin/ | Django 自带后台 + ModelAdmin 配置 |
| 学生 | /login/ | 自己写视图 + 模板(用 authenticate / login) |
本章 4 个核心收获
- OneToOneField 把业务表挂到 auth_user 上——不要自己重新写用户表
- ModelAdmin 子类给后台加列表 / 筛选 / 搜索,几行配置就能用
- Group + Permission = RBAC,给"角色"配权限而不是给"人"
- authenticate / login / logout / request.user——Django 自带的认证 4 件套
课堂练习
两组共 12 题:第一组业务模型 / 后台,第二组权限和认证。
模型与后台(6 题)
为什么本章不直接 username = CharField 加在 Student 模型里,而要 OneToOneField 关联到 User?
"一个学生有多次成绩"在模型里用什么字段实现?
想让后台列表页显示某些列,应该设置哪个 ModelAdmin 属性?
verbose_name = '学生' 起什么作用?
第一次跑 migrate 之前可以先 createsuperuser,效果一样。
本章 14 张表里,auth_* / django_* 开头的表都是 Django 自动建的,不用我们写代码。
django.contrib.auth / contenttypes / sessions 等内置应用一起装好。我们只需写自己的业务模型(Student / Teacher / Score)。权限与认证(6 题)
Django 后台的 RBAC 模型由哪三层构成?
老师能登录 /admin/ 后台,关键勾选项是?
"老师不该能删除学生"——怎么实现?
视图里 authenticate(request, username=..., password=...) 干了什么?
login(request, user) 把它写进 session。两步分离的设计很关键——你能在校验通过但其它条件不满足时不登录。登录之后,视图里怎么拿当前用户?
is_authenticated 为 False)。学生模型用 request.user.student 反查——这是 OneToOneField 的反向访问,由 Django 自动生成。
Student.user = OneToOneField(User) 后,从 User 反向访问 Student 用类名小写(user.student)。如果学生不存在,会抛 Student.DoesNotExist。