当前位置:   article > 正文

Django框架开发学生管理系统_dajigo框架用户管理系统的设计与实现

dajigo框架用户管理系统的设计与实现

Django框架开发学生管理系统

学生管理系统是针对学校的大量业务处理工作而开发的管理软件,主要用于学生信息和学生分数的管理。总体任务,是实现学生信息管理关系的科学化、系统化、规范化和自动化,方便教务人员使用计算机对学生各种信息进行日常管理,如查询、修改、增加、删除等,此外还涉及学生自主查询成绩等功能。

13.1需求分析

学生管理系统包含3个角色,分别是后台管理员、老师和学生。所以该系统应该具备以下功能:

1.后台管理员拥有本系统的最高权限,可以创建分组、设定权限、管理老师信息等。

2.老师具备登陆后台功能,并且可以修改原始密码。

3.老师具备管理学生功能,可以对学生信息进行增删改查。

4.老师具备管理成绩功能,可以对学生成绩进行增删改查。

5.老师具备批量上传功能,可以批量上传学生信息,批量上传成绩信息等。

5.学生具备查看成绩功能,可以查看所有参加考试的成绩。

13.2系统功能是设计

13.2.1系统功能结构

请添加图片描述

13.2.2系统业务流程

请添加图片描述

13.2.3系统预览

系统分为前台后台。前台主要用于学生帐户查询成绩,后台系统主要用于老师账户上传学生信息和成绩信息,以及管理员账户添加老师信息和设置权限组。

在前台登录页面,学生账户通过学号和密码等登录成功后,进入前台首页,首页主要展示该学生的所有成绩信息。单机某门课程考试选项,即可查看该门课程成绩信息。

在后台登陆页面,老师账户通过邮箱密码登陆后,即可进入老师管理页面。

在后台登陆页面,管理员团账户通过邮箱密码登陆成功后即可进入管理员管理页面。

13.3系统开发必备

13.3.1系统开发环境

本系统的软件开发及运行环境如下:

操作系统:Windows7及以上、linux

数据库和驱动;MySQL+PyMySQL

开发IDE:pycharm

开发框架:Django3 + Bootstrap + jQuery

浏览器:Chrome浏览器

13.3.2文件夹组织结构

本项目使用的主要命令

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  # 创建开发服务器
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

请添加图片描述

13.4数据库设计

13.4.1数据库概要说明

本系统使用Mysql数据库来存储数据,数据库名为student_system,共包含14张数据表,其中auth和django为前缀的表哦都是django框架自动创建的表,其余为用户需要创建的数据表。

请添加图片描述

studen_system数据库中的数据表对应的中文表名及主要作用:

请添加图片描述

13.4.2数据表模型

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 = "学生信息"
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37

在上述代码中,使用model.OneToOneField实现student表和auth_user表的一对一管,使用models.ForeignKey实现student和teacher的一对多关联。此外,数据表也存在其他表之间的关系:
请添加图片描述

13.5公共模块设计

13.5.1修改目录结构

使用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()
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21

创建config包,将asgi,settings,urls,wsgi文件移入该包,并修改settings

ROOT_URLCONF = "config.urls"

......

WSGI_APPLICATION = 'config.wsgi.application'
  • 1
  • 2
  • 3
  • 4
  • 5

13.5.2配置settings

config/settings.py文件是项目的配置文件,需要进行以下配置。

(1)创建应用:

diango-admin startapp 应用名称
  • 1

本项目主要创建四个应用: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'
]
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

(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
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

(3)配置mysql数据库:

DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.mysql',
        'NAME': 'student_system',
        'USER': 'root',
        'PASSWORD': '********'
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

(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',}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17

13.6学生模块设计

13.6.1学生登录功能实现

学号是一个学生在学校的唯一标识,所以在前台登录页面,需要学生填写学号及密码进行登录。对于学生填写的学号和密码信息要分别进行验证。例如:学好是否为空、长度范围是否满足、学好是否存在、学号密码是否匹配。

实现学生登录功能的步骤如下。

(1)创建登陆表单。

为了更加方便地验证表单,可以继承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
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43

上述代码中,定义了一个以"clean_" + 属性名的方法clean_student_mum(),它是Django Form中的特殊方法,用于对该属性进行验证。clean_student_mum()方法就是用于都student_num属性进行验证,

如果不存在,就会抛出异常

(2)创建模板文件

在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">&times;</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 %}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41

在上述代码中,使用extends继承父模板,然后替换掉block中内容。在html页面中,{{form.student_num}}是页面的学好元素,{{form.student_num.errors}}是页面中检测学号的错误信息。

(3)创建视图

在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})  # 渲染模板
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30

上述代码,get()方法比较简单,主要用于渲染登陆页面。在post()方法中,首先使用表单类接受表单数据,然后调用is_valid()函数来验证用户提交的表单数据是否满足StudentLoginForm类中设置的验证规则。如果不能满足验证规则,则在登陆页面中输出设置的错误信息。如果满足验证规则,则使用authenticate()函数判断学号和密码是否匹配。如果不匹配没提示错误信息;如果匹配,则将图画信息保存到session中。

在浏览器输入127.0.0.1:8000/login进入登录页面

请添加图片描述

13.6.2推出登录功能实现

登陆成功后,网站顶部导航栏会显示该学生的姓名。滑动鼠标到姓名位置,将显示注销和修改密码菜单,单击注销即可退出登录。使用django内置的logout()函数可以快速实现退出功能,成功后,页面跳转到accounts/login在settings.py文件中可以通过设置LOGIN_URL参数更改跳转路径:

LOGIN_URL = "/login/"
  • 1

在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")
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

在上述代码中,由于视图函数logout()与模块下的logout重名,所以引入时设置别名,当调用django_logout()函数时,会清楚response对象cookie和django_session中的记录,从而实现退出登陆的功能。

13.6.3查询成绩功能的实现

学生登录成功后,页面将跳转至成绩列表页。该页面需要学生登录后才能访问。此时可以使用django提供的装饰器函数login_required()来判断是否登陆成功。如果学生已经登陆,此函数返回True,否则返回False。通常情况下,需要在settings.py文件中设置LOGIN_URL参数,代码如下:

LOGIN_URL = "/login/"
  • 1

成绩列表页会展示该学生所有考试成绩列表信息,包括考试名称。成绩上传时间、所在班级以及老师名。由于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})
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

在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 %}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18

单机每一科成绩,即可根据成绩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})
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

同时创建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 = "成绩信息"
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30

13.7后台管理员模块设计

管理员具有网站的最高权限,在本项目中只为其设计管理老师信息和设置权限功能。创建管理员的命令如下:

python manage.py createsuperuser
  • 1

请添加图片描述

13.7.1管理老师信息

为了实现管理员具有后台管理老师信息的功能,需要在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)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73

上述代码中,定义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表中对应的记录也会一并删除,反之亦然。

13.7.2设置权限组

添加完老师信息以后,需要魏老师用户设置权限,该权限包括管理学生信息、管理考试成绩信息、批量导入学生信息和成绩信息等。

(1)单机后台首页中的“组”进入权限组管理,单机“增加组”按钮进入权限组设置,然后添加组名称,并选中该族所具备的权限:

请添加图片描述

单机“用户”进入用户列表,单击选择老师邮箱,进入用户详情页面,在"可用组"列表中将“老师”添加到右侧的“选中的组”列表中:

请添加图片描述

13.8老师模块设计

在auth_user表中,老师用户的is_staff字段值为1,所以老师用户可以通过用户名和密码登录后台。

登陆成功以后,该用户就具备老师用户组的的权限,可以管理学生信息、管理成绩信息和批量上传以及修改密码,

请添加图片描述

13.8.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)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80

上述代码中,由于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 = '班级名称'
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16

学生信息列表如图所示:

请添加图片描述

13.8.2管理成绩信息

为了实现老师在后台管理成绩信息的功能,需要在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)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25

student和score是一对多的关系,所以在添加学生信息时需要选择学生姓名:

请添加图片描述

添加完成信息后,页面没有跳转到成绩列表页。在该页面中可以根据考试名称、学生姓名以及学号进行查询。由于score表中没有学生姓名和学号字段,所以不能直接在search_fields属性中设置,但是可以通过score表中的student外键关联到student表,使用双下划线+属性名的方式设置,代码如下:

search_field = (["title","student__name","student__student_num"])
  • 1

成绩列表如图:
请添加图片描述

13.8.3批量上传学生信息和成绩信息

当学生信息和成绩信息较多时,使用手动上传的方式会非常繁琐,并且难以保证上传的准确率。此时老师用户可以使用批量上传的方式来结局这个问题。那么数据从哪里获得呢?通常情况下,可以将学生信息和成绩信息先保存到Excel文件中,然后在后台导入Excel文件,将Excel数据存储到Mysql数据库中,从而实现批量上传的目的。

学生信息:

请添加图片描述

成绩信息:
请添加图片描述

uploadfile用于实现批量上传功能,具体步骤如下

(1)编写uploadfile/models.py文件

设置允许上传的文件后缀为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 = "上传文件"
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22

在上传文件页面,如果上传其他格式文件(jpg等),单击提交,将提示”必须为xls或xlsx文件“错误信息,如图所示:

请添加图片描述

(2)编写uploadfile/admin.py文件

接受上传的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
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20

上述代码中,如果上传的是学生信息内容,则调用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
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33

批量添加学生信息运行效果图如图所示:

请添加图片描述

如果上传的是成绩信息内容,则调用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
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
最后绑定:
admin.site.register(FileUpload, FileUploadAdmin)
  • 1

效果图:

请添加图片描述

源码:https://gitee.com/laoliNb666/ss_manager_by_django请添加图片描述
请添加图片描述

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/IT小白/article/detail/154042
推荐阅读
相关标签
  

闽ICP备14008679号