赞
踩
学生管理系统是针对学校的大量业务处理工作而开发的管理软件,主要用于学生信息和学生分数的管理。总体任务,是实现学生信息管理关系的科学化、系统化、规范化和自动化,方便教务人员使用计算机对学生各种信息进行日常管理,如查询、修改、增加、删除等,此外还涉及学生自主查询成绩等功能。
学生管理系统包含3个角色,分别是后台管理员、老师和学生。所以该系统应该具备以下功能:
1.后台管理员拥有本系统的最高权限,可以创建分组、设定权限、管理老师信息等。
2.老师具备登陆后台功能,并且可以修改原始密码。
3.老师具备管理学生功能,可以对学生信息进行增删改查。
4.老师具备管理成绩功能,可以对学生成绩进行增删改查。
5.老师具备批量上传功能,可以批量上传学生信息,批量上传成绩信息等。
5.学生具备查看成绩功能,可以查看所有参加考试的成绩。
系统分为前台后台。前台主要用于学生帐户查询成绩,后台系统主要用于老师账户上传学生信息和成绩信息,以及管理员账户添加老师信息和设置权限组。
在前台登录页面,学生账户通过学号和密码等登录成功后,进入前台首页,首页主要展示该学生的所有成绩信息。单机某门课程考试选项,即可查看该门课程成绩信息。
在后台登陆页面,老师账户通过邮箱密码登陆后,即可进入老师管理页面。
在后台登陆页面,管理员团账户通过邮箱密码登陆成功后即可进入管理员管理页面。
本系统的软件开发及运行环境如下:
操作系统:Windows7及以上、linux
数据库和驱动;MySQL+PyMySQL
开发IDE:pycharm
开发框架:Django3 + Bootstrap + jQuery
浏览器:Chrome浏览器
本项目使用的主要命令
python manager.py makemigrations # 生成数据库迁移脚本
python manager.py migrate # 根据makemigrations命令生成的脚本,创建或修改数据库表结构
python manager.py migrate migrate_name # 回滚到指定迁移版本
python manager.py collectstaic # 生成静态资源目录,根据settings.py中的STATIC_ROOT设置
python manager.py shell # 打开django解释器,引入项目包
python manager.py dbshell # 打开django数据库连接,执行原生SQL命令
python manager.py startproject # 创建一个Django项目
python manager.py startapp # 常见一个app
python manager.py createsuperuser # 创建一个管理器超级用户,使用django.contrib.auth认证
python manager.py runserver # 创建开发服务器
本系统使用Mysql数据库来存储数据,数据库名为student_system,共包含14张数据表,其中auth和django为前缀的表哦都是django框架自动创建的表,其余为用户需要创建的数据表。
studen_system数据库中的数据表对应的中文表名及主要作用:
django框架自带的ORM可以满足绝大多数数据库开发的需求,在没有达到一定的数量级时,完全不需要担心ORM为项目带来的瓶颈。下面是学生管理系统使用ORM管理一个学生模块的数据模型:关键代码
class Student(CreateUpdateMixin): student_num = models.CharField(max_length=10, unique=True, verbose_name='学号') name = models.CharField(max_length=20, help_text='name/姓名', verbose_name='姓名') gender = models.CharField(max_length=32, choices=(('male', '男'), ('female', '女')), default='男', help_text='gender/性别', verbose_name='性别') phone = models.CharField(max_length=11, help_text="phone/联系电话", verbose_name='联系电话') birthday = models.DateField(verbose_name="出生日期") # user表一对一关联 user = models.OneToOneField(User, on_delete=models.CASCADE) # teacher表以对多关联 teacher = models.ForeignKey(Teacher, on_delete=models.CASCADE) # 设置外键 def __str__(self): return self.name def teacher_name(self): """ 获取老师名称 """ self.verbose_name = '老师名称' return self.teacher.name teacher_name.short_description = '老师名称' def class_name(self): """ 获取班级名称 """ return self.teacher.class_name class_name.short_description = '班级名称' class Meta: db_table = "student" verbose_name_plural = "学生信息" verbose_name = "学生信息"
在上述代码中,使用model.OneToOneField实现student表和auth_user表的一对一管,使用models.ForeignKey实现student和teacher的一对多关联。此外,数据表也存在其他表之间的关系:
使用django命令创建项目时,会生成一个与项目同名的全局配置文件目录
将manage.py修改如下:
#!/usr/bin/env python """Django's command-line utility for administrative tasks.""" import os import sys def main(): os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'config.settings') try: from django.core.management import execute_from_command_line except ImportError as exc: raise ImportError( "Couldn't import Django. Are you sure it's installed and " "available on your PYTHONPATH environment variable? Did you " "forget to activate a virtual environment?" ) from exc execute_from_command_line(sys.argv) if __name__ == '__main__': main()
创建config包,将asgi,settings,urls,wsgi文件移入该包,并修改settings
ROOT_URLCONF = "config.urls"
......
WSGI_APPLICATION = 'config.wsgi.application'
config/settings.py文件是项目的配置文件,需要进行以下配置。
(1)创建应用:
diango-admin startapp 应用名称
本项目主要创建四个应用:teacher、student、score、uploadfile。创建完成后,将名称写入settings文件的INSTALLED_APPS中。
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'teacher',
'student',
'score',
'uploadfile'
]
(2)配置时区:
# LANGUAGE_CODE = 'en-us'
LANGUAGE_CODE = 'zh-Hans'
# TIME_ZONE = 'UTC'
TIME_ZONE = 'Asia/Shanghai'
USE_I18N = True
USE_L10N = True
USE_TZ = True
(3)配置mysql数据库:
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.mysql',
'NAME': 'student_system',
'USER': 'root',
'PASSWORD': '********'
}
}
(4)自定义配置:
# 文件上传路径 MEDIA_ROOT = os.path.join(BASE_DIR, 'uploads') # 设置初始密码 TEACHRE_INIT_PASSWORD = 't123456' # 老师账号的初始密码 STUDENT_INIT_PASSWORD = '123456' # 学生账号的初始密码 # 没有登录时跳转的URL LOGIN_URL = '/login/' # 兼容Bootstrap Alert 样式 from django.contrib.messages import constants as message_constants MESSAGE_TAGS = {message_constants.DEBUG: 'debug', message_constants.INFO: 'info', message_constants.SUCCESS: 'success', message_constants.WARNING: 'warning', message_constants.ERROR: 'danger',}
学号是一个学生在学校的唯一标识,所以在前台登录页面,需要学生填写学号及密码进行登录。对于学生填写的学号和密码信息要分别进行验证。例如:学好是否为空、长度范围是否满足、学好是否存在、学号密码是否匹配。
实现学生登录功能的步骤如下。
为了更加方便地验证表单,可以继承form.Form类来验证表单,在stuent目录下创建forms.py文件,创建StudentLoginForm类,并对学号和密码设置验证规则。
from django import forms from django.contrib.auth.models import User class StudentLoginForm(forms.Form): student_num = forms.CharField( lable='学号', required=True, max_length=11, widget=forms.TextInput(attrs={ 'class': "form-control mb-0", 'placeholder': '请输入学号' }), error_messages={ 'required': "学号不能为空", "max_length": '长度不能超过50个字符', } ) password = forms.CharField( label='密码', required=True, min_length=6, max_length=50, widget=forms.TextInput(attrs={ 'class': "form-control mb-0", 'placeholder': '请输入密码' }), error_messages={ 'required': "学号不能为空", "max_length": '长度不能超过50个字符', "min_length": '长度不能少于6个字符', } ) # 二次验证函数的名字是固定写法,以clean_开头,后面跟上字段的变量 def clean_student_num(self): # 通过了validators的验证后,在进行二次验证 student_num = self.cleaned_data['student_num'] try: User.objects.get(username=student_num) # 使用student_num获取django用户 except User.DoesNotExist: raise forms.ValidationError("学号不存在", 'invalid') else: return student_num
上述代码中,定义了一个以"clean_" + 属性名的方法clean_student_mum(),它是Django Form中的特殊方法,用于对该属性进行验证。clean_student_mum()方法就是用于都student_num属性进行验证,
如果不存在,就会抛出异常
在templates文件夹中创建login.html模板文件,关键代码如下:
{% extends "base.html" %} {% block title %}登录页面{% endblock %} {% block content %} <div class="main-content container"> <div class="inner-content"> <div class="xxdj-report border zycs_text"> <div class="course-report"> <h1>学生登录</h1> <div class="wid775 div-course"> {% if messages %} {% for message in messages %} <div class="alert alert-{{ message.tags }} alert-dismissible fade show" role="alert"> <strong>{{ message }}</strong> <button type="button" class="close" data-dismiss="alert" aria-label="Close"> <span aria-hidden="true">×</span> </button> </div> {% endfor %} {% endif %} <form class="mt-4" action="" method="post"> {% csrf_token %} <div class="form-group"> <label>{{form.student_num.label}}</label> {{form.student_num}} {{form.student_num.errors}} </div> <div class="form-group"> <label>{{form.password.label}}</label> {{form.password}} {{form.password.errors}} </div> <div class="d-inline-block w-100"> <button type="submit" class="btn btn-primary float-right">登录</button> </div> </form> </div> </div> </div> </div> </div> {% endblock %}
在上述代码中,使用extends继承父模板,然后替换掉block中内容。在html页面中,{{form.student_num}}是页面的学好元素,{{form.student_num.errors}}是页面中检测学号的错误信息。
在student/views.py中创建一个基于类的视图StudentLoginView(),并创建get()和post()请求,用于显示登陆页面和提交登陆表单,关键代码如下:
class StudentLoginView(View): """ 学生登录页表单 """ def get(self, request): """ 显示登录页面 """ return render(request, 'login.html', {'form': StudentLoginForm()}) # 渲染模板 def post(self, request): """ 提交登录页面表单 """ form = StudentLoginForm(request.POST) # 接收Form表单 # 验证表单 if form.is_valid(): student_num = request.POST['student_num'] # 获取学号 password = request.POST['password'] # 获取密码 user = authenticate(request, username=student_num, password=password) # 授权校验 if user is not None: # 校验成功,获得返回用户信息 login(request, user) # 登录用户,设置登录session request.session['uid'] = user.id # 设置用户名的session request.session['username'] = user.student.name # 设置用户名的session request.session['student_num'] = user.student.student_num # 设置用户名的session return HttpResponseRedirect('/') else: messages.add_message(request, messages.ERROR, '用户名和密码不匹配') # 提示错误信息 return render(request, 'login.html', {'form': form}) # 渲染模板
上述代码,get()方法比较简单,主要用于渲染登陆页面。在post()方法中,首先使用表单类接受表单数据,然后调用is_valid()函数来验证用户提交的表单数据是否满足StudentLoginForm类中设置的验证规则。如果不能满足验证规则,则在登陆页面中输出设置的错误信息。如果满足验证规则,则使用authenticate()函数判断学号和密码是否匹配。如果不匹配没提示错误信息;如果匹配,则将图画信息保存到session中。
在浏览器输入127.0.0.1:8000/login进入登录页面
登陆成功后,网站顶部导航栏会显示该学生的姓名。滑动鼠标到姓名位置,将显示注销和修改密码菜单,单击注销即可退出登录。使用django内置的logout()函数可以快速实现退出功能,成功后,页面跳转到accounts/login在settings.py文件中可以通过设置LOGIN_URL参数更改跳转路径:
LOGIN_URL = "/login/"
在student/views.py文件中,定义logout()函数来实现用户退出功能,关键代码如下
from django.contrib.auth import authenticate, login,logout as django_logout
def logout(request):
"""
退出登录
:param request:
:return:
"""
django_logout(request) # 清楚response的cookie和django_session中的记录
return HttpResponseRedirect("/login")
在上述代码中,由于视图函数logout()与模块下的logout重名,所以引入时设置别名,当调用django_logout()函数时,会清楚response对象cookie和django_session中的记录,从而实现退出登陆的功能。
学生登录成功后,页面将跳转至成绩列表页。该页面需要学生登录后才能访问。此时可以使用django提供的装饰器函数login_required()来判断是否登陆成功。如果学生已经登陆,此函数返回True,否则返回False。通常情况下,需要在settings.py文件中设置LOGIN_URL参数,代码如下:
LOGIN_URL = "/login/"
成绩列表页会展示该学生所有考试成绩列表信息,包括考试名称。成绩上传时间、所在班级以及老师名。由于student表和score表是一对多的管理,所以可以通过学生信息直接获取该学生的所有成绩信息,然后再模板中遍历每一个成绩信息即可。
在student/views.py中创建index()函数,关键代码如下:
@login_required
def index(request):
"""
首页
:param request:
:return:
"""
student_num = request.session.get("student_num", ) # 获取当前学生学号
student = Student.objects.get(student_num=student_num) # 根据学号查询学生信息
scores = student.score_set.all() # 获取该学生的所有分数
return render(request, 'index.html', {'scores': scores})
在templates下创建index.html文件
{% if not scores %} <div style="text-align:center"> 暂无成绩信息! </div> {% else %} {% for score in scores %} <li style="border: 1px solid #ccc;border-radius: 10px;margin:10px;"> <a href="{% url 'score' score_id=score.id %}"> <span class="start_time"><b>{{ score.created_at|date:"Y-m" }}</b><i>{{ score.created_at|date:"d" }}</i></span> <h1 title="{{ score.title }}"> {{ score.title }} </h1> <p>班级:{{score.student.teacher.class_name}}</p> <p style="padding:5px 0">老师:{{score.student.teacher.name}}</p> </a> </li> {% endfor %} {% endif %}
单机每一科成绩,即可根据成绩id进入该科成绩的详情页,在详情页需要显示学生名、考试名称和学生成绩等内容,如果成绩id不存在,则进入404错误页面,在student/views.py目录中创建score()函数,关键代码如下:
@login_required
def score(request, score_id):
"""
成绩详情
:param request:
:param score_id:
:return:
"""
try:
score= Score.object.get(id=score_id)
except:
return render(request,'404.html', {"errmsg":'数据异常'})
return render(request,'score.html',{'score':score})
同时创建score表结构:
from django.db import models from utils.base_models import CreateUpdateMixin from student.models import Student class Score(CreateUpdateMixin): title = models.CharField(max_length=20,help_text='title/考试名称',verbose_name='考试名称') score = models.DecimalField(max_digits=5,decimal_places=2,help_text='score/分数',verbose_name='分数') student = models.ForeignKey(Student, on_delete=models.CASCADE,verbose_name='学生姓名') # 设置外键 def student_name(self): """ 获取学生姓名 """ self.verbose_name = '学生姓名' return self.student.name student_name.short_description = '学生姓名' def student_num(self): """ 获取学号 """ self.verbose_name = '学号' return self.student.student_num student_num.short_description = '学号' class Meta: db_table = "score" verbose_name_plural = "成绩信息" verbose_name = "成绩信息"
管理员具有网站的最高权限,在本项目中只为其设计管理老师信息和设置权限功能。创建管理员的命令如下:
python manage.py createsuperuser
为了实现管理员具有后台管理老师信息的功能,需要在teacher应用中编写teacher/admin.py文件对后台老师模块进行设置。创建TeacherAdmin类,继承admin.ModelAdmin父类。在TeacherAdmin类中定义相应的类属性,如list_display用于配置展示列表的字段;list_filter用于配置过滤查询字段;search_fields用于配置搜索字段等。配置完成后,需要使用admin.site.register(Teacher, TeacherAdmin)将Teacher模型类绑定到TeacherAdmin管理后台。关键代码如下:
from django.contrib import admin from teacher.models import Teacher from django.contrib.auth.models import User from django.conf import settings from django.contrib.auth.hashers import make_password # Register your models here. class TeacherAdmin(admin.ModelAdmin): # 配置展示列表,在Teacher板块下方列表展示 list_display = ('name', 'email', 'class_name', 'gender', 'phone') # 配置过滤查询字段,在Teacher板块右侧显示过滤框 list_filter = ('class_name', 'name') # 配置可以搜索的字段,在Teacher板块下方显示搜索框 search_fields = (['class_name', 'name']) # 定义后台列表显示时每页显示的数量 list_per_page = 30 # 定义列表显示的顺序 ordering = ('-create_at',) # 显示字段 fieldsets = ( (None, { 'fields': ('name', 'email', 'class_name', 'gender', 'phone') }), ) def save_model(self, request, obj, form, change): user = User.objects.create( email=request.POST.get("email"), # 获取邮箱 username=request.POST.get("email"), # 防止重名,用Email作为用户登陆名 password=make_password(settings.TEACHER_INIT_PASSWORD), # 密码加密 is_staff=1 # 郧西作为管理员登陆后台 ) obj.tid_obj.user_id = user.id # 获取新增用户的id,作为tid和user_id super().save_model(request, obj, form, change) return def delete_queryset(self, request, queryset): """ 删除多条数据 同时删除user表中数据 由于使用的是批量删除,所以需要遍历delete_queryset 中的 queryset :param request: :param queryset: :return: """ for obj in queryset: obj.user.delete() super().delete_model(request, obj) return def delete_model(self, request, obj): """ 删除单条记录 同时删除user表中数据 :param request: :param obj: :return: """ super().delete_model(request, obj) if obj.user: obj.user.delete() return # 设置后台页面头部显示内容和页面标题 admin.site.site_header = "学生管理系统" admin.site.site_site = "学生管理系统" # 绑定Teacher模型到TeacherAdmin管理后台 admin.site.register(Teacher, TeacherAdmin)
上述代码中,定义save_model()方法用来覆盖父类的save_model()方法,是先保存老师信息的同时添加到auth_user表。定义delete_queryset()方法用来覆盖父类的delete_queryset()方法,实现批量删除老师信息的同时,删除auth_user表中的用户信息。定义delete_model()方法覆盖父类方法,实现删除单个用户信息的同时,删除auth_user表中的用户信息。
在网站后台首页,单机“老师信息”即可进入老师管理页面,
由于teacher表和auth_user表是一对一的关系,添加老师后,也会在auth_user表中和新增一条用户信息。为防止老师重名,将teacher表的邮箱作为auth_user表的用户名,并且设置初始密码为setting.py文件中settings.TEACHER_INIT_PASSWORD的值。
此外,由于设置了级联删除,当删除teacher表的数据以后,auth_user表中对应的记录也会一并删除,反之亦然。
添加完老师信息以后,需要魏老师用户设置权限,该权限包括管理学生信息、管理考试成绩信息、批量导入学生信息和成绩信息等。
(1)单机后台首页中的“组”进入权限组管理,单机“增加组”按钮进入权限组设置,然后添加组名称,并选中该族所具备的权限:
单机“用户”进入用户列表,单击选择老师邮箱,进入用户详情页面,在"可用组"列表中将“老师”添加到右侧的“选中的组”列表中:
在auth_user表中,老师用户的is_staff字段值为1,所以老师用户可以通过用户名和密码登录后台。
登陆成功以后,该用户就具备老师用户组的的权限,可以管理学生信息、管理成绩信息和批量上传以及修改密码,
为了实现老师在后台管理学生信息的功能,需要在student应用中编写student/admin.py文件对后台学生模块进行设置。创建StudentAdmin类,继承admin.ModelAdmin父类。StudentAdmin类的实现与TeacherAdmin类似:
from django.contrib import admin from student.models import Student from django.contrib.auth.models import User from django.conf import settings from django.contrib.auth.hashers import make_password # Register your models here. class StudentAdmin(admin.ModelAdmin): """ 创建StudentAdmin类,继承自admin.ModelAdmin """ # 配置展示列表,在user板块下方列表展示 list_display = ('student_num', 'name', 'class_name', 'teacher_name', 'gender', 'birthday') # 配置过滤查询字段,在User板块右侧显示过滤框 list_filter = ('name', 'student_name') # 配置可以搜索的字段,在User板块下方显示搜索框 search_fields = (['name', 'student_name']) readonly_fields = ("teacher",) # 设置只读字段,不允许更改 ordering = ('-created_at',) # 定义列表现实的顺序,符号表示降序 # 显示字段 fieldsets = ( (None, { 'fields': ('student_num', 'name', 'gender', 'phone', 'birthday') }), ) def save_model(self, request, obj, form, change): """ 添加student表时,同时添加到user表 由于需要和teacher表级联,所以自动获取当前登陆的老师id作为teacher_id :param request: :param obj: :param form: :param change: :return: """ if not change: user = User.objects.create( username=request.POST.get("student_num"), # 学号作为用户登陆名 password=make_password(settings.STUDENT_INIT_PASSWORD), ) obj.user_id = user.id # 获取新增用户的id obj.teacher_id = request.user.id # 获取当前老师的id super().save_model(request, obj, form, change) # 调用父类保存方法 return def delete_queryset(self, request, queryset): """ 删除多条数据 同时删除user表中数据 由于使用的是批量删除,所以需要遍历delete_queryset 中的 queryset :param request: :param queryset: :return: """ for obj in queryset: obj.user.delete() super().delete_model(request, obj) return def delete_model(self, request, obj): """ 删除单条记录 同时删除user表中数据 :param request: :param obj: :return: """ super().delete_model(request, obj) if obj.user: obj.user.delete() return # 绑定Student模型到StudentAdmin管理后台 admin.site.register(Student, StudentAdmin)
上述代码中,由于teacher表和student表是一对多关系,所以在auth_user表中设置了teacher_id字段,
该字段的值即为当前登录的老师用户id。
老师账号登陆管理后台后,但学生信息右侧的”增加“按钮,即可添加学生信息
单击”保存“按钮,页面会跳转到学列表页,并显示学生所在的”班级名称“和”老师姓名“。因为student表中没有这两个字段,所以需要在student/model.py中编写如下代码
def teacher_name(self): """ 获取老师名称 """ self.verbose_name = '老师名称' return self.teacher.name teacher_name.short_description = '老师名称' def class_name(self): """ 获取班级名称 """ return self.teacher.class_name class_name.short_description = '班级名称'
学生信息列表如图所示:
为了实现老师在后台管理成绩信息的功能,需要在score应用中编写score/admin.py文件对后台成绩模块进行设置。创建ScoreAdmin类,继承自admin.ModelAdmin父类。score/admin.py:
from django.contrib import admin from score.models import Score class ScoreAdmin(admin.ModelAdmin): """ 创建ScoreAdmin类,继承自admin.ModelAdmin """ # 配置展示列表,在Score板块下方列表显示 list_display = ('title', "student_num", 'student', 'score') # 配置过滤查询字段,在Score板块右侧显示过滤框 list_filter = ('title', 'student') # 配置可以搜索的字段,在Score板块下方显示搜索框 # student是外键,管理student类,这里使用双下划线+属性名的方式搜索 search_fields = (['title', 'student__name', 'student__student_num']) ordering = ('-created_at',) # 定义列表显示的顺序,负号为降序 fieldsets = ( (None, { 'fields': ('title', 'student', 'score') }), ) # 绑定Scope模型到ScoreAdmin管理后台 admin.site.register(Score, ScoreAdmin)
student和score是一对多的关系,所以在添加学生信息时需要选择学生姓名:
添加完成信息后,页面没有跳转到成绩列表页。在该页面中可以根据考试名称、学生姓名以及学号进行查询。由于score表中没有学生姓名和学号字段,所以不能直接在search_fields属性中设置,但是可以通过score表中的student外键关联到student表,使用双下划线+属性名的方式设置,代码如下:
search_field = (["title","student__name","student__student_num"])
成绩列表如图:
当学生信息和成绩信息较多时,使用手动上传的方式会非常繁琐,并且难以保证上传的准确率。此时老师用户可以使用批量上传的方式来结局这个问题。那么数据从哪里获得呢?通常情况下,可以将学生信息和成绩信息先保存到Excel文件中,然后在后台导入Excel文件,将Excel数据存储到Mysql数据库中,从而实现批量上传的目的。
学生信息:
成绩信息:
uploadfile用于实现批量上传功能,具体步骤如下
设置允许上传的文件后缀为xls或xlsx,并且定义上传的文件内容是学生信息或成绩信息,关键代码如下
from django.db import models from utils.base_models import CreateUpdateMixin from django.core import validators from teacher.models import Teacher TYPE_CHOICES = ( (1, '学生信息'), (2, '成绩信息'), ) class FileUpload(CreateUpdateMixin): file_name = models.FileField( validators=[validators.FileExtensionValidator(['xls', 'xlsx'], message='必须为xls或xlsx文件')], help_text='file_type/上传文件名', verbose_name='上传文件名') file_type = models.IntegerField(choices=TYPE_CHOICES, default=1, help_text='file_type/文件类型', verbose_name='文件类型') teacher = models.ForeignKey(Teacher, on_delete=models.CASCADE) # 设置外键 class Meta: db_table = "file" verbose_name_plural = "上传文件" verbose_name = "上传文件"
在上传文件页面,如果上传其他格式文件(jpg等),单击提交,将提示”必须为xls或xlsx文件“错误信息,如图所示:
接受上传的Excel文件,然后使用openpyxl库读取Excel数据,并写入数据库,关键代码如下
class FileUploadAdmin(admin.ModelAdmin): # 配置展示列表,在User版块下的列表展示 list_display = ('file_name',) # 设置只读字段,不允许更改 readonly_fields = ('teacher',) def save_model(self, request, obj, form, change): obj.teacher_id = request.user.id # 获取当前老师的ID # 调用父类方法保存 super().save_model(request, obj, form, change) # 拼接目录 file_path = os.path.join(settings.MEDIA_ROOT, obj.file_name.name) if request.POST['file_type'] == '1': # 上传学生信息 repetition = self.upload_student(file_path, request.user.id) elif request.POST['file_type'] == '2': # 上传成绩信息 repetition = self.upload_score(file_path) # 提示重复数据条数 if repetition: messages.add_message(request, messages.INFO, f'过滤{repetition}条重复数据') return
上述代码中,如果上传的是学生信息内容,则调用upload_student()方法。upload_student()方法中使用openpyxl模块读取Excel中的每一行学生信息。首先判断该学号的用户是否存在,如果已经存在,则不添加学生信息;如果不存在,则把该学生信息写入auth_user表,并且使用bulk_create()方法批量添加到student表中,upload_student()方法代码如下:
def upload_student(self, file_path, teacher_id): wb = openpyxl.load_workbook(file_path) # 打开excel ws = wb.active # 选中第一个sheet rows = ws.max_row # 获取行数 columns = ws.max_column # 获取列数 student_list = [] repetition = 0 # 从第2行开始遍历每行 for row in ws.iter_rows(min_row=2, min_col=1, max_row=rows, max_col=columns): data = [i.value for i in row] # 获取每一行数据 # 去除重复数据 if Student.objects.filter(student_num=data[0]).exists(): repetition += 1 continue # 写入User表 user = User( username=data[0], # 以学号作为用户名,防止重复 password=make_password(settings.STUDENT_INIT_PASSWORD), ) user.save() # 存入数据库 # 写入student表 student = Student( student_num=data[0], name=data[1].strip(), # 去除空格 gender='male' if data[2] == "男" else "femal", phone=data[4], birthday=data[3], user_id=user.id, teacher_id=teacher_id ) student_list.append(student) Student.objects.bulk_create(student_list) # 批量加入Student表 return repetition
批量添加学生信息运行效果图如图所示:
如果上传的是成绩信息内容,则调用upload_score()方法。upload_score()方法中使用,openpyxl模块读取Excel中的每一行成绩信息。首先判断学号是否存在,如果不存在,咋不天界学生成绩信息;如果存在,则获取该用户id,然后查找score表中是否还有该学生的成绩信息,并且使用bulk_create()方法批量添加到score表中。upload_score()方法的关键代码如下:
def upload_score(self, file_path): wb = openpyxl.load_workbook(file_path) # 打开excel ws = wb.active # 选中第一个sheet rows = ws.max_row # 获取行数 columns = ws.max_column # 获取列数 score_list = [] repetition = 0 # 从第2行开始遍历每行 for row in ws.iter_rows(min_row=2, min_col=1, max_row=rows, max_col=columns): data = [i.value for i in row] # 获取每一行数据 # 查找student表,获取student_id try: student = Student.objects.get(student_num=data[1]) except: continue # 去除重复数据 if Score.objects.filter(title=data[0], student_id=student.id).exists(): repetition += 1 continue # 写入student表 score = Score( title=data[0], # 标题 student_id=student.id, # 学生id score=data[-1] # 学生分数 ) score_list.append(score) Score.objects.bulk_create(score_list) # 批量加入Score表 return repetition
admin.site.register(FileUpload, FileUploadAdmin)
效果图:
源码:https://gitee.com/laoliNb666/ss_manager_by_django
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。