20222415 2024-2025-2 《Python程序设计》实验四报告
20222415 2024-2025-2 《Python程序设计》实验四报告
学生成绩管理系统
一、实验内容
(1)选择自己喜欢的Web开发技术(框架),编程实现一个『学生成绩管理系统』,搭建Web前、后端开发环境,实现一个基于B/S架构的『学生成绩管理系统』;
(2)『学生成绩管理系统』的功能要求描述如下:
①用户的注册、登录和管理(用户的增加、删除、修改、查询);
②课程的管理(课程的增加、删除、修改、查询);
③成绩的录入和查询(某同学某门课程的成绩录入和查询,以及总成绩汇总查询);
④设定管理员、普通用户两种角色,普通用户具有管理课程权限、成绩的录入和查询(功能②③),管理员具有全部权限(功能①②③);
⑤进行简单的数据表设计,选择自己熟悉的数据库,综合利用前、后端以及数据库编程,实现一个完整的基于B/S架构的简单Web应用。
二、实验设计
(1)工具选择
①Django3.22
Django 是一个用于构建 Web 应用程序的高级 Python Web 框架,其采用了Django 采用了 MVT 的软件设计模式,即模型(Model),视图(View)和模板(Template)。
②mysql
③Navicat
数据库图形用户界面管理
(2)数据库分析
关系模式:
用户(用户名,账号密码,账号类型)
学生(学号,学生姓名,性别,出生年月,机关,专业,班级,用户ID)
教师(工号,名字,性别,出生年月,用户ID)
专业(专业代码,专业名)
班级(班级编号,班级名,专业)
课程(课程编码,课程名,授课教师)
成绩(学号,课程号,成绩)
(3)功能模块划分
①登录模块
系统不对外开放注册功能,只允许管理员手动添加用户,但用户可以使用已有账户登录系统,在登录时会判断用户身份进入相应的功能界面
②管理员管理模块
使用管理员账号登录后可进入管理员管理界面,此模块包含用户账号注册、查看、修改、删除功能,学生添加、查看、修改、删除功能,教师添加、查看、修改、删除,专业添加、查看、修改、删除功能,课程添加、查看、修改、删除功能,成绩查看、删除功能。
③学生模块
学生可以查看并且编辑自己的信息,查看自己的课表,查看自己的成绩,修改自己的密码
④教师模块
老师可以查看并且编辑自己的信息,查看自己的课表,录入学生成绩,修改自己的密码
三、系统实现
整体文件路径如下:
(1)账号登录
使用Python manage.py startapp account
命令创建一个app,名为account,用来管理账号,账号可以有三种(学生、老师、管理员),
定义一个模型 账号(id,用户名,密码,账号类型,创建时间,更新时间)
accout/modles.py
class Account(models.Model):
Area_Level = (
(0, 'student'),
(1, 'teacher'),
(2, 'manager'),
)
id = models.AutoField(primary_key=True)
username = models.CharField(max_length=40, null=False, verbose_name="用户名", unique=True) # 账号
password = models.CharField(max_length=512, null=False, verbose_name="密码") # 密码
customer_type = models.IntegerField(default=0, choices=Area_Level, verbose_name="账号类型") # 类型
create_time = models.DateTimeField('保存日期', default=timezone.now)
update_time = models.DateTimeField('修改日期', default=timezone.now)
accout/views.py
def login(request):
if request.method == 'GET':
return render(request, 'login.html')
elif request.method == 'POST':
username = request.POST.get('username', None)
password = request.POST.get('password', None)
if not username or not password:
return render(request, 'login.html', context={'messages': ['提交的信息不完整']})
if not Account.objects.filter(username=username).exists():
return render(request, 'login.html', context={'messages': ['用户不存在']})
account = Account.objects.filter(username=username)[0]
if not check_password(password, account.password):
return render(request, 'login.html', context={'messages': ['密码错误']})
request.session['is_login'] = 'true'
request.session['username'] = account.username
request.session['user_id'] = account.id
request.session['customer_type'] = account.customer_type
if account.customer_type == 0:
return redirect('student_info')
elif account.customer_type == 1:
return redirect('teacher_info')
return redirect('manager_index')
登录页面
(2)学生页面
学生可以查看并且编辑自己的信息,查看自己的课表,查看自己的成绩,修改自己的密码。
使用Python manage.py startapp student
命令创建一个学生app,定义一个学生模型来存放学生信息,
student/models.py
class StudentInfo(models.Model):
id = models.AutoField(primary_key=True)
student_id = models.BigIntegerField(verbose_name="学号")
name = models.CharField(max_length=255, verbose_name="学生名字")
sex = models.CharField(max_length=10, verbose_name="性别")
birth_day = models.CharField(max_length=255, verbose_name="出生年月")
native_place = models.CharField(max_length=255, verbose_name="籍贯")
major = models.CharField(max_length=255, verbose_name="专业")
clazz = models.CharField(max_length=255, verbose_name='班级')
user_id = models.ForeignKey(Account, on_delete=models.CASCADE, verbose_name="用户ID")
create_time = models.DateTimeField('保存日期', default=timezone.now)
update_time = models.DateTimeField('修改日期', default=timezone.now)
创建一个视图,用来管理学生页面的请求,根据请求中携带的typ参数,判断是哪一个页面(个人信息页,成绩页,课表页面,修改密码页面)
student/views.py
@check_login
@transaction.atomic
def student_info(request):
user_id = request.session.get('user_id')
req_typ = request.GET.get('typ')
if request.method == 'GET':
if not req_typ:
if not StudentInfo.objects.filter(user_id=user_id).exists():
return render(request, 'student_info.html',
context={'student_id': '未设置', 'name': '未设置', 'sex': '未设置', 'birth_day': '未设置',
'native_place': '未设置', 'major': '未设置', 'clazz': '未设置', 'title': '个人信息'})
info = StudentInfo.objects.get(user_id=user_id)
return render(request, 'student_info.html', context=info.__dict__)
elif req_typ.upper() == 'update'.upper():
context = {'typ': 'update', 'title': '修改个人信息'}
if StudentInfo.objects.filter(user_id=user_id).exists():
context['sid'] = StudentInfo.objects.get(user_id=user_id).id
context.update(get_all_majors_clazzs())
return render(request, 'student_info.html', context=context)
elif req_typ.upper() == 'change_password'.upper():
return render(request, 'student_info.html', context={'typ': 'change_password', 'title': '修改密码'})
if request.method == 'POST':
params = request.POST.dict()
if params.get('req_typ') == 'info':
if not params.get('name') or not params.get('sex') or not params.get('birth_day'):
context = {'typ': 'update', 'message': '提交的数据不完整', 'title': '修改个人信息'}
context.update(get_all_majors_clazzs())
return render(request, 'student_info.html', context=context)
major, clazz = params['clazz'].split('-')
exists = False
if StudentInfo.objects.filter(user_id=Account.objects.get(id=user_id)).exists():
student = StudentInfo.objects.get(user_id=Account.objects.get(id=user_id))
student.name = params['name']
student.sex = params['sex']
student.birth_day = params['birth_day']
student.native_place = params['native_place']
student.major = major
student.clazz = clazz
exists = True
else:
student = StudentInfo(name=params['name'], sex=params['sex'], birth_day=params['birth_day'],
native_place=params['native_place'], major=major, clazz=clazz,
student_id=params.get('student_id', get_random_str(14, True)),
user_id=Account.objects.get(id=user_id)
)
student.save()
_clazz = Clazz.objects.get(name=clazz, major_id__name=major)
if exists:
cs = ClazzStudents.objects.get(student_id=student.id)
cs.clazz_id = _clazz
cs.save()
else:
ClazzStudents(student_id=student, clazz_id=_clazz).save()
return redirect('/student_info/')
elif params.get('req_typ') == 'password':
password = params.get('password')
password_again = params.get('password-again')
if len(str(password)) < 8:
return render(request, 'student_info.html',
context={'typ': 'change_password', 'message': '密码长度不足8位' , 'title': '修改密码'})
if password_again != password:
return render(request, 'student_info.html',
context={'typ': 'change_password', 'message': '两次输入的密码不一致', 'title': '修改密码'})
account = Account.objects.get(id=user_id)
account.password = make_password(password)
account.save()
return render(request, 'student_info.html', context={'typ': 'change_password', 'message': '修改密码完成', 'title': '修改密码'})
学生页面如下
(3)管理员管理页面
管理员可以查看所有专业、添加专业、查看所有班级、添加班级、查看所有课程、添加课程、查看所有学生信息、修改学生信息、添加学生账号、查看所有老师信息、修改老师信息、添加老师账号、查看学生成绩。
使用Python manage.py startapp manager
命令创建管理app,
然后创建一系列的表:
①专业表
class Major(models.Model):
"""
专业
"""
id = models.BigAutoField(primary_key=True)
major_code = models.CharField(max_length=32, verbose_name='专业代码', unique=True)
name = models.CharField(max_length=32, verbose_name='专业名', unique=True)
create_time = models.DateTimeField('保存日期', default=timezone.now)
update_time = models.DateTimeField('修改日期', default=timezone.now)
班级表,这是存储每个班级信息的,这里设置了联合唯一索引,就是班级名和专业(外键)
class Clazz(models.Model):
"""
班级
"""
id = models.BigAutoField(primary_key=True)
name = models.CharField(max_length=32, verbose_name='班级名')
major_id = models.ForeignKey(Major, on_delete=models.CASCADE)
create_time = models.DateTimeField('保存日期', default=timezone.now)
update_time = models.DateTimeField('修改日期', default=timezone.now)
class Meta:
unique_together = ('name', 'major_id',)
班级学生表,这是存储班级包含了哪些学生,这里基本都是外键,学生信息表的外键,班级表的外键。并且联合唯一约束是学生和班级,因为一个学生只能出现在一个班级里面。
Class ClazzStudents(models.Model):
"""
班级学生表
"""
id = models.BigAutoField(primary_key=True)
student_id = models.ForeignKey(StudentInfo, on_delete=models.CASCADE)
clazz_id = models.ForeignKey(Clazz, on_delete=models.CASCADE)
create_time = models.DateTimeField('保存日期', default=timezone.now)
update_time = models.DateTimeField('修改日期', default=timezone.now)
class Meta:
unique_together = ('student_id', 'clazz_id',)
课程表,存储学校开设了哪些课程,老师是老师信息表的外键。联合唯一约束是课程名和老师。
class Curriculum(models.Model):
"""
课程信息
"""
id = models.BigAutoField(primary_key=True)
name = models.CharField(max_length=255, verbose_name='课程名')
teacher_id = models.ForeignKey(TeacherInfo, on_delete=models.CASCADE) # 老师信息
create_time = models.DateTimeField('保存日期', default=timezone.now)
update_time = models.DateTimeField('修改日期', default=timezone.now)
class Meta:
unique_together = ('name', 'teacher_id',)
学生成绩表,存储学生各科成绩的表,使用外键进行关联
class StudentCurriculumScore(models.Model):
"""
学生成绩表
"""
id = models.BigAutoField(primary_key=True)
student_id = models.ForeignKey(StudentInfo, on_delete=models.CASCADE)
curriculum_id = models.ForeignKey(Curriculum, on_delete=models.CASCADE)
score = models.DecimalField(decimal_places=1, max_digits=10)
create_time = models.DateTimeField('保存日期', default=timezone.now)
update_time = models.DateTimeField('修改日期', default=timezone.now)
class Meta:
unique_together = ('student_id', 'curriculum_id')
在视图manager/views.py中定义外所有的数据表,功能太多,具体见源代码和演示视频,管理员界面见下
(4)连接数据库
连接数据库需要在student_manager_system/setting.py中定义,修改DATABASES字段,定义如下:
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.mysql',
'NAME': 'student_management_sys',#连接数据库的名称
'USER': 'ruki',#连接的用户名
'PASSWORD': 'openGauss@123',#密码
'HOST': 'localhost',#数据库所在的IP,我数据库在自己电脑上,所以用的本地连接
'PORT': '3306',
}
}
四、程序代码
https://gitee.com/rukiting/pythontest/tree/sutdent-manager-sys-main/sutdent-manager-sys-main
五、问题及解决办法
这次实验遇见的问题很多,很难一一赘述,小问题大问题层出不穷,可以说,这个实验我就是在解决问题的过程中完成的,虽然问题很多,大问题小问题杂在一起,连我自己都很难说清,但是这些问题的解决办法却是一以贯之的,那就是面对问题不逃避。对于一个接触代码快三年的学生,我很难说在这近三年我的编程能力提升了多少,或者说因为大二时AI的进步,我自己动手编程能力还不如大一的时候,但是在心态上我进步了很多,不再对写代码产生畏难情绪,遇见了问题也不像大一刚开始那样很迷茫,不知道从哪改起,现在我已经能很淡然地接受在电脑前一坐一晚上,一改一整天,我觉得这是我这么多次大大小小地实验做下来,最大的收获。
六、课程总结
(1)课程总结
上课的时候我有种感受就是好老师的教学思路是不是互通啊,本课程的学习进程让我梦回C语言的学习历程。
先是每一门编程语言都会有的hello,world,让我初次接触这门语言,在我的电脑上搭建起一块属于python的天地,然后开始学习语法、控制语句、字符串操作,在学习这些语言共同的东西后,开始学习python与从C有别的特色产品,面向对象和模块都是我之前没有接触过的东西,这些特色产品让我的python天地看起来更专业、实用。在C语言课的最后,我学习了文件操作,又在数据库课上学习了怎么用C语言编写学生管理系统,大三选修嵌入式,又学习了使用C语言进行SOCKET编程。两者路径重合过高,确实也减轻了我的学习负担,让我学起来更加轻松。
(2)课程感想
虽然选上python课并非我的本意,或者说选上python课称得上我两年选课以来最大的失误,毕竟应该不会有人在公选课已经选够21分之后,又再选一门在完全没课的一天晚上连上三节的公选课,如果有,那不会是我。但是事情就是这么奇怪的,刚刚好让我在激烈的选课之中,一连选上两门竞争激烈的选修课程,在我惨烈的抢课战绩上增添了浓墨重彩的两笔,刚刚好我在确定我抢上了我想要的专选课之后就再也没打开过选课系统确认过我的课程,两个刚刚好凑成了我选课经历中的最大失误,成功让我在连续两学期选课破防之后,再破防一学期。我在这门课上最大的收获却是选好课之后一定好好好确认自己有没有多选上其他课程,以及教室的网络比寝室的网真的快很多,适合抢课。
虽然选上python并非我愿,但是python确实不负盛名,一是Python这门语言不愧在TIOBE霸榜多年,是真的简单易学好上手,二是python这门课是真的不愧在同学之间的好口碑,是真的轻松好学有意思。