首页 > 编程语言 >【课程设计/实训作业】python学生成绩管理系统源码

【课程设计/实训作业】python学生成绩管理系统源码

时间:2024-04-01 15:29:05浏览次数:39  
标签:课程设计 varchar title python request 源码 modal const data

项目介绍

一直想做一款学生成绩管理系统,看了很多优秀的开源项目但是发现没有合适的。于是利用空闲休息时间开始自己写了一套管理系统。学习过程中遇到问题可以咨询评论。

在线体验

http://score.gitapp.cn/

(账号:admin123 密码:admin123)

源码地址

https://github.com/geeeeeeeek/python_score

功能介绍

  • 学生管理:管理系统可以录入、修改和查询学生的基本信息,如学生名称、备注等。
  • 成绩管理:成绩信息的增删改查。
  • 班级管理:班级信息的增删改查。
  • 课程管理:课程信息的增删改查。
  • 用户管理:管理和浏览网站的用户信息,可以新增、编辑和删除用户。
  • 系统信息:管理员可以查看系统的基本信息,包括系统名称、服务器信息、内存信息、cpu信息、软件信息等。

界面预览

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

开发环境

  • 后端: Python 3.8 + Django 3.2
  • 前端: Javascript + Vue
  • 数据库:MySQL 5.7
  • 开发平台:Pycharm + vscode
  • 运行环境:Windows 10/11

关键技术

  • 前端技术栈 ES6、vue、vuex、vue-router、vue-cli、axios、antd
  • 后端技术栈 Python、Django、pip

后端技术

django框架

Django是一款基于Python开发的全栈式一体化Web应用框架。2003年问世之初,它只是美国一家报社的内部工具,2005年7月使用BSD许可证完成了开源。Django采用MTV设计模式,即Model(模型)+ Template(模板)+ View(视图)。它遵循MVC设计,并且内置了对象关系映射(ORM)层,使得开发者无需关心底层的数据存取细节,可以更专注于业务逻辑的开发。

Django的目的是削减代码量,简单且迅速地搭建以数据库为主体的复杂Web站点。它是全栈式框架,因此安装起来很简单,而且使用者众多。这使得Django除具有完备的官方文档之外,还有大量的关联文档、丰富的第三方库可供使用。与其他框架相比,Django用起来要轻松得多。

优点:

  • 提供了定义序列化器Serializer的方法。可以快速根据Django ORM或者其他库自动序列化或反序列化。
  • 提供了丰富的类视图MIXIN扩展类。可以简化视图的编写。
  • 具有丰富的定制层级。包括函数视图、类视图,还可以将视图与自动生成API结合,满足各种需求。
  • 支持多种身份认证和权限认证方式。
  • 内置了限流系统。

前端技术

  • npm:node.js的包管理工具,用于统一管理我们前端项目中需要用到的包、插件、工具、命令等,便于开发和维护。
  • ES6:Javascript的新版本,ECMAScript6的简称。利用ES6我们可以简化我们的JS代码,同时利用其提供的强大功能来快速实现JS逻辑。
  • vue-cli:Vue的脚手架工具,用于自动生成Vue项目的目录及文件。
  • vue-router: Vue提供的前端路由工具,利用其我们实现页面的路由控制,局部刷新及按需加载,构建单页应用,实现前后端分离。
  • vuex:Vue提供的状态管理工具,用于统一管理我们项目中各种数据的交互和重用,存储我们需要用到数据对象。
  • Ant-design:基于MVVM框架Vue开源出来的一套前端ui组件。

运行步骤

后端运行步骤

(1) 安装mysql数据库,启动服务

(2) 打开cmd命令行,进入mysql,并新建数据库

mysql -u root -p
CREATE DATABASE IF NOT EXISTS python_score DEFAULT CHARSET utf8 COLLATE utf8_general_ci;

(3) 恢复sql数据

use xxx
source xxxx.sql

(4) 修改settings.py中的配置信息

(5) 安装python 3.8

(6) 安装依赖包

pip install -r requirements.txt -i https://mirrors.aliyun.com/pypi/simple

(7) 运行命令

python manage.py runserver 

前端运行步骤

(1) 安装node 16

(2) cmd进入web目录下,安装依赖,执行:

npm install 

(3) 运行项目

npm run dev

代码结构

后端结构

server  
├── myapp            // 主应用
│       └── auth                     // 认证管理
│       └── middlewares              // 中间件
│       └── permission               // 权限
│       └── views                    // 视图与接口(主要业务代码)
│       └── models.py                // 状态码
│       └── serializers.py           // 状态码
│       └── urls.py                  // 状态码
│       └── utils.py                 // 状态码
├── entity             // 实体类
├── interceptor        // 拦截器
├── mapper             // 数据库映射
├── server             // 配置目录
├── upload             // 静态资源目录
├── requiements.txt    // 依赖项

前端结构

├── build                      // 构建相关  
├── public                     // 公共文件
│   ├── favicon.ico            // favicon图标
│   └── index.html             // html模板
├── src                        // 源代码
│   ├── api                    // 所有请求
│   ├── assets                 // 主题 字体等静态资源
│   ├── router                 // 路由
│   ├── store                  // 全局 store管理
│   ├── utils                  // 全局公用方法
│   ├── views                  // view界面
│   ├── App.vue                // 入口页面
│   ├── main.js                // 入口 加载组件 初始化等
│   └── settings.js            // 系统配置
├── .eslintignore              // 忽略语法检查
├── .eslintrc.js               // eslint 配置项
├── .gitignore                 // git 忽略项
├── babel.config.js            // babel.config.js
├── package.json               // package.json
└── vite.config.js             // vue配置

数据库设计

需求分析

在学生管理系统中,需要存储和管理学生信息、评论信息、分类信息、标签信息、用户信息、通知信息、日志信息。

实体设计如下:

  • 学生(thing)
  • 成绩(score)
  • 班级(classification)
  • 课程(course)
  • 用户(user)
  • 日志(log)

关系如下:

  • 一个学生有一个班级
  • 一个班级可以对应多个学生

数据表设计如下:



// 成绩管理
Table score {
    score_id int [pk]
    thing_id int [ref: > thing.thing_id]  // 学生
    course_id int [ref: > course.course_id]  // 课程
    score varchar // 分数
    create_time datetime
 }

// 学生管理
Table thing {
    thing_id int [pk]
    classification_id int [ref: > C.classification_id]
    title varchar // 姓名
    sex varchar // 性别
    age varchar // 年龄
    mobile varchar // 手机号
    status int  // 0在读 1毕业
    remark varchar // 备注
    create_time datetime
 }

 
 // 课程管理
 Table course {
   course_id int [pk]
   title varchar //  课程名称
   teacher_name varchar //  主讲教师
   create_time datetime
 }
 
 // 班级管理
 Table classification as C {
   classification_id int [pk]
   title varchar //  
   create_time datetime
 }
 
 
 
 Table user {
   user_id int [pk]
   role varchar // 1管理员 2普通用户 3演示帐号
   status int // 0正常 1封号
   username varchar
   password varchar
   nickname varchar
   avatar varchar
   description varchar
   wish int [ref: <> thing.thing_id]
   email varchar
   mobile varchar
   score int // 积分
   push_email varchar // 推送邮箱
   push_switch int  // 推送开关
   token varchar
   admin_token varchar
 }
 
 
 Table login_log {
   log_id int [pk]
   username varchar
   ip varchar
   log_time datetime
 }
 
 // 操作日志
 Table op_log {
   id int [pk]
   re_ip varchar
   re_time datetime
   re_url varchar
   re_method varchar
   re_content varchar
   access_time varchar
 }
 
 // 异常日志
 Table error_log {
   id int [pk]
   ip varchar
   method varchar
   content varchar
   log_time varchar
 }
 

开发过程

无论是成绩管理、学生管理、用户管理、班级管理、课程管理、日志管理等功能都是基于springboot+vue框架开发的,开发流程是:

  • 第一步:编写实体
  • 第二步:编写序列化层
  • 第三步:编写views层
  • 第四步:编写界面和API

下面用学生管理功能来演绎这个流程,其它的管理功能都是这个流程。

第一步:编写实体类

在server下的myapp下的models.py下面新建Thing类。并写入如下代码:

class Thing(models.Model):
    STATUS_CHOICES = (
        ('0', '上架'),
        ('1', '下架'),
    )
    id = models.BigAutoField(primary_key=True)
    classification = models.ForeignKey(Classification, on_delete=models.CASCADE, blank=True, null=True,
                                       related_name='classification_thing')
    tag = models.ManyToManyField(Tag, blank=True)
    title = models.CharField(max_length=100, blank=True, null=True)
    cover = models.ImageField(upload_to='cover/', null=True)
    description = models.TextField(max_length=1000, blank=True, null=True)
    price = models.CharField(max_length=50, blank=True, null=True) 
    mobile = models.CharField(max_length=50, blank=True, null=True)
    age = models.CharField(max_length=10, blank=True, null=True) 
    location = models.CharField(max_length=50, blank=True, null=True) 
    status = models.CharField(max_length=1, choices=STATUS_CHOICES, default='0')
    create_time = models.DateTimeField(auto_now_add=True, null=True)
    pv = models.IntegerField(default=0)
    recommend_count = models.IntegerField(default=0)
    wish = models.ManyToManyField(User, blank=True, related_name="wish_things")
    wish_count = models.IntegerField(default=0)
    collect = models.ManyToManyField(User, blank=True, related_name="collect_things")
    collect_count = models.IntegerField(default=0)

    class Meta:
            db_table = "b_thing"

第二步:编写序列化层

在server下的myapp下的serializers.py下新建ThingSerializer类,并写入代码:

class ThingSerializer(serializers.ModelSerializer):
    # 额外字段
    classification_title = serializers.ReadOnlyField(source='classification.title')

    class Meta:
        model = Thing
        fields = '__all__'

第三步:编写views层

在server的myapp下的views下,新建Thing.py代码,并写入代码,实现增删改查


# 查
@api_view(['GET'])
def list_api(request):
    if request.method == 'GET':
        keyword = request.GET.get("keyword", None)
        c = request.GET.get("c", None)
        tag = request.GET.get("tag", None)
        if keyword:
            things = Thing.objects.filter(title__contains=keyword).order_by('create_time')
        elif c:
            classification = Classification.objects.get(pk=c)
            things = classification.classification_thing.all()
        elif tag:
            tag = Tag.objects.get(id=tag)
            print(tag)
            things = tag.thing_set.all()
        else:
            things = Thing.objects.all().order_by('create_time')

        serializer = ThingSerializer(things, many=True)
        return APIResponse(code=0, msg='查询成功', data=serializer.data)

# 删
@api_view(['GET'])
def detail(request):

    try:
        pk = request.GET.get('id', -1)
        thing = Thing.objects.get(pk=pk)
    except Thing.DoesNotExist:
        utils.log_error(request, '对象不存在')
        return APIResponse(code=1, msg='对象不存在')

    if request.method == 'GET':
        serializer = ThingSerializer(thing)
        return APIResponse(code=0, msg='查询成功', data=serializer.data)

# 增
@api_view(['POST'])
@authentication_classes([AdminTokenAuthtication])
def create(request):

    if isDemoAdminUser(request):
        return APIResponse(code=1, msg='演示帐号无法操作')

    serializer = ThingSerializer(data=request.data)
    if serializer.is_valid():
        serializer.save()
        return APIResponse(code=0, msg='创建成功', data=serializer.data)
    else:
        print(serializer.errors)
        utils.log_error(request, '参数错误')

    return APIResponse(code=1, msg='创建失败')

# 改
@api_view(['POST'])
@authentication_classes([AdminTokenAuthtication])
def update(request):

    if isDemoAdminUser(request):
        return APIResponse(code=1, msg='演示帐号无法操作')

    try:
        pk = request.GET.get('id', -1)
        thing = Thing.objects.get(pk=pk)
    except Thing.DoesNotExist:
        return APIResponse(code=1, msg='对象不存在')

    serializer = UpdateThingSerializer(thing, data=request.data)
    if serializer.is_valid():
        serializer.save()
        return APIResponse(code=0, msg='查询成功', data=serializer.data)
    else:
        print(serializer.errors)
        utils.log_error(request, '参数错误')

    return APIResponse(code=1, msg='更新失败')

# 删
@api_view(['POST'])
@authentication_classes([AdminTokenAuthtication])
def delete(request):

    if isDemoAdminUser(request):
        return APIResponse(code=1, msg='演示帐号无法操作')

    try:
        ids = request.GET.get('ids')
        ids_arr = ids.split(',')
        Thing.objects.filter(id__in=ids_arr).delete()
    except Thing.DoesNotExist:
        return APIResponse(code=1, msg='对象不存在')
    return APIResponse(code=0, msg='删除成功')


然后将该接口添加到urls.py中即可。

第四步:编写界面和API

打开前端web工程,在views文件夹下新建thing.vue文件,并编写代码:

<template>
  <div>
    <!--页面区域-->
    <div class="page-view">
      <div class="table-operations">
        <a-space>
          <a-button type="primary" @click="handleAdd">新增</a-button>
          <a-button @click="handleBatchDelete">批量删除</a-button>
          <a-input-search addon-before="名称" enter-button @search="onSearch" @change="onSearchChange" />
        </a-space>
      </div>
      <a-table
        size="middle"
        rowKey="id"
        :loading="data.loading"
        :columns="columns"
        :data-source="data.dataList"
        :scroll="{ x: 'max-content' }"
        :row-selection="rowSelection"
        :pagination="{
          size: 'default',
          current: data.page,
          pageSize: data.pageSize,
          onChange: (current) => (data.page = current),
          showSizeChanger: false,
          showTotal: (total) => `共${total}条数据`,
        }"
      >
        <template #bodyCell="{ text, record, index, column }">
          <template v-if="column.key === 'operation'">
            <span>
              <a @click="handleEdit(record)">编辑</a>
              <a-divider type="vertical" />
              <a-popconfirm title="确定删除?" ok-text="是" cancel-text="否" @confirm="confirmDelete(record)">
                <a href="#">删除</a>
              </a-popconfirm>
            </span>
          </template>
        </template>
      </a-table>
    </div>

    <!--弹窗区域-->
    <div>
      <a-modal
        :visible="modal.visile"
        :forceRender="true"
        :title="modal.title"
        width="880px"
        ok-text="确认"
        cancel-text="取消"
        @cancel="handleCancel"
        @ok="handleOk"
      >
        <template #footer>
          <a-button key="back" @click="handleCancel">取消</a-button>
          <a-button key="submit" type="primary" :loading="submitting" @click="handleOk">确认</a-button>
        </template>
        <div style="padding-right: 16px; max-height: 480px; overflow-x: hidden; overflow-y: auto">
          <a-form ref="myform" :label-col="{ style: { width: '80px' } }" :model="modal.form" :rules="modal.rules">
            <a-row :gutter="24">
              <a-col span="12">
                <a-form-item label="姓名" name="title">
                  <a-input placeholder="请输入" v-model:value="modal.form.title" />
                </a-form-item>
              </a-col>
              <a-col span="12">
                <a-form-item label="班级名称" name="classification">
                  <a-select
                    placeholder="请选择"
                    allowClear
                    :options="modal.cData"
                    :field-names="{ label: 'title', value: 'id' }"
                    v-model:value="modal.form.classification"
                  />
                </a-form-item>
              </a-col>
              <a-col span="12">
                <a-form-item label="性别" name="sex">
                  <a-input placeholder="请输入" v-model:value="modal.form.sex" />
                </a-form-item>
              </a-col>
              <a-col span="12">
                <a-form-item label="年龄" name="age">
                  <a-input placeholder="请输入" v-model:value="modal.form.age" />
                </a-form-item>
              </a-col>
              <a-col span="12">
                <a-form-item label="手机号" name="mobile">
                  <a-input placeholder="请输入" v-model:value="modal.form.mobile" />
                </a-form-item>
              </a-col>
              <a-col span="12">
                <a-form-item label="备注" name="remark">
                  <a-input placeholder="请输入" v-model:value="modal.form.remark" />
                </a-form-item>
              </a-col>
              <a-col span="12">
                <a-form-item label="状态" name="status">
                  <a-select placeholder="请选择" allowClear v-model:value="modal.form.status">
                    <a-select-option key="0" value="0">在读</a-select-option>
                    <a-select-option key="1" value="1">毕业</a-select-option>
                  </a-select>
                </a-form-item>
              </a-col>
            </a-row>
          </a-form>
        </div>
      </a-modal>
    </div>
  </div>
</template>

<script setup lang="ts">
  import { FormInstance, message, SelectProps } from 'ant-design-vue';
  import { createApi, listApi, updateApi, deleteApi } from '/@/api/thing';
  import { listApi as listClassificationApi } from '/@/api/classification';
  import { listApi as listTagApi } from '/@/api/tag';
  import { BASE_URL } from '/@/store/constants';
  import { FileImageOutlined, VideoCameraOutlined } from '@ant-design/icons-vue';

  const columns = reactive([
    {
      title: '序号',
      dataIndex: 'index',
      key: 'index',
      width: 60,
    },
    {
      title: '姓名',
      dataIndex: 'title',
      key: 'title',
    },
    {
      title: '性别',
      dataIndex: 'sex',
      key: 'sex',
    },
    {
      title: '年龄',
      dataIndex: 'age',
      key: 'age',
    },
    {
      title: '班级',
      dataIndex: 'classification_title',
      key: 'classifaction_title',
    },
    {
      title: '手机号',
      dataIndex: 'mobile',
      key: 'mobile',
    },
    {
      title: '状态',
      dataIndex: 'status',
      key: 'status',
      customRender: ({ text, record, index, column }) => (text === '0' ? '在读' : '毕业'),
    },
    {
      title: '备注',
      dataIndex: 'remark',
      key: 'remark',
    },
    {
      title: '操作',
      dataIndex: 'action',
      key: 'operation',
      align: 'center',
      fixed: 'right',
      width: 140,
    },
  ]);

  const beforeUpload = (file: File) => {
    // 改封面文件名
    const fileName = new Date().getTime().toString() + '.' + file.type.substring(6);
    const copyFile = new File([file], fileName);
    console.log(copyFile);
    modal.form.imageFile = copyFile;
    return false;
  };

  // 文件列表
  const fileList = ref<any[]>([]);

  const submitting = ref<boolean>(false);

  // 页面数据
  const data = reactive({
    dataList: [],
    loading: false,
    keyword: '',
    selectedRowKeys: [] as any[],
    pageSize: 10,
    page: 1,
  });

  // 弹窗数据源
  const modal = reactive({
    visile: false,
    editFlag: false,
    title: '',
    cData: [],
    tagData: [{}],
    form: {
      id: undefined,
      title: undefined,
      classification: undefined,
      sex: undefined,
      age: undefined,
      address: undefined,
      mobile: undefined,
      remark: undefined,
      status: undefined,
    },
    rules: {
      title: [{ required: true, message: '请输入', trigger: 'change' }],
      classification: [{ required: true, message: '请选择', trigger: 'change' }],
      status: [{ required: true, message: '请选择状态', trigger: 'change' }],
    },
  });

  const myform = ref<FormInstance>();

  onMounted(() => {
    getDataList();
    getCDataList();
    getTagDataList();
  });

  const getDataList = () => {
    data.loading = true;
    listApi({
      keyword: data.keyword,
    })
      .then((res) => {
        data.loading = false;
        console.log(res);
        res.data.forEach((item: any, index: any) => {
          item.index = index + 1;
        });
        data.dataList = res.data;
      })
      .catch((err) => {
        data.loading = false;
        console.log(err);
      });
  };

  const getCDataList = () => {
    listClassificationApi({}).then((res) => {
      modal.cData = res.data;
    });
  };
  const getTagDataList = () => {
    listTagApi({}).then((res) => {
      res.data.forEach((item, index) => {
        item.index = index + 1;
      });
      modal.tagData = res.data;
    });
  };

  const onSearchChange = (e: Event) => {
    data.keyword = e?.target?.value;
    console.log(data.keyword);
  };

  const onSearch = () => {
    getDataList();
  };

  const rowSelection = ref({
    onChange: (selectedRowKeys: (string | number)[], selectedRows: DataItem[]) => {
      console.log(`selectedRowKeys: ${selectedRowKeys}`, 'selectedRows: ', selectedRows);
      data.selectedRowKeys = selectedRowKeys;
    },
  });

  const handleAdd = () => {
    resetModal();
    modal.visile = true;
    modal.editFlag = false;
    modal.title = '新增';
    // 重置
    for (const key in modal.form) {
      modal.form[key] = undefined;
    }
    modal.form.cover = undefined;
  };
  const handleEdit = (record: any) => {
    resetModal();
    modal.visile = true;
    modal.editFlag = true;
    modal.title = '编辑';
    // 重置
    for (const key in modal.form) {
      modal.form[key] = undefined;
    }
    for (const key in record) {
      if (record[key]) {
        modal.form[key] = record[key];
      }
    }
    // if (modal.form.cover) {
    //   modal.form.coverUrl = BASE_URL + modal.form.cover;
    //   modal.form.cover = undefined;
    // }
  };

  const confirmDelete = (record: any) => {
    console.log('delete', record);
    deleteApi({ ids: record.id })
      .then((res) => {
        getDataList();
      })
      .catch((err) => {
        message.error(err.msg || '操作失败');
      });
  };

  const handleBatchDelete = () => {
    console.log(data.selectedRowKeys);
    if (data.selectedRowKeys.length <= 0) {
      console.log('hello');
      message.warn('请勾选删除项');
      return;
    }
    deleteApi({ ids: data.selectedRowKeys.join(',') })
      .then((res) => {
        message.success('删除成功');
        data.selectedRowKeys = [];
        getDataList();
      })
      .catch((err) => {
        message.error(err.msg || '操作失败');
      });
  };

  const handleOk = () => {
    myform.value
      ?.validate()
      .then(() => {
        const formData = new FormData();
        if (modal.editFlag) {
          formData.append('id', modal.form.id);
        }
        formData.append('title', modal.form.title || '');
        formData.append('sex', modal.form.sex || '');
        formData.append('age', modal.form.age || '');
        formData.append('address', modal.form.address || '');
        formData.append('mobile', modal.form.mobile || '');
        formData.append('remark', modal.form.remark || '');
        if (modal.form.classification) {
          formData.append('classification', modal.form.classification);
        }
        if (modal.form.imageFile) {
          formData.append('cover', modal.form.imageFile);
        }
        if (modal.form.status) {
          formData.append('status', modal.form.status);
        }
        if (modal.editFlag) {
          submitting.value = true;
          updateApi(
            {
              id: modal.form.id,
            },
            formData,
          )
            .then((res) => {
              submitting.value = false;
              hideModal();
              getDataList();
            })
            .catch((err) => {
              submitting.value = false;
              console.log(err);
              message.error(err.msg || '操作失败');
            });
        } else {
          submitting.value = true;
          createApi(formData)
            .then((res) => {
              submitting.value = false;
              hideModal();
              getDataList();
            })
            .catch((err) => {
              submitting.value = false;
              console.log(err);
              message.error(err.msg || '操作失败');
            });
        }
      })
      .catch((err) => {
        console.log('不能为空');
      });
  };

  const handleCancel = () => {
    hideModal();
  };

  // 恢复表单初始状态
  const resetModal = () => {
    myform.value?.resetFields();
    fileList.value = [];
  };

  // 关闭弹窗
  const hideModal = () => {
    modal.visile = false;
  };
</script>

<style scoped lang="less">
  .page-view {
    min-height: 100%;
    background: #fff;
    padding: 24px;
    display: flex;
    flex-direction: column;
  }

  .table-operations {
    margin-bottom: 16px;
    text-align: right;
  }

  .table-operations > button {
    margin-right: 8px;
  }
</style>

这就是学生管理功能的实现流程,其它的功能管理实现一模一样的。按照这个流程编写即可。

重要模块实现

分页实现

基于ant-design框架的a-table的分页插件。

// 分页变量

  const data = reactive({
    dataList: [],
    loading: false,
    keyword: '',
    selectedRowKeys: [] as any[],
    pageSize: 10,
    page: 1,
  });
  
// 分页插件
:pagination="{
          size: 'default',
          current: data.page,
          pageSize: data.pageSize,
          onChange: (current) => (data.page = current),
          showSizeChanger: false,
          showTotal: (total) => `共${total}条数据`,
        }"

请求工具实现

前端的请求工具是基于axios开发的,位于utils的http文件夹中。封装了request请求和拦截器。

const service: AxiosInstance = axios.create({
  // baseURL: import.meta.env.BASE_URL + '',
  baseURL: BASE_URL + '',
  timeout: 15000,
});

// axios实例拦截请求
service.interceptors.request.use(
  (config: InternalAxiosRequestConfig) => {
    config.headers.ADMINTOKEN = localStorage.getItem(ADMIN_USER_TOKEN);
    config.headers.TOKEN = localStorage.getItem(USER_TOKEN);

    return config;
  },
  (error: AxiosError) => {
    return Promise.reject(error);
  },
);

// axios实例拦截响应
service.interceptors.response.use(
  (response: AxiosResponse) => {
    if (response.status == 200) {
      if (response.data.code == 0 || response.data.code == 200) {
        return response;
      } else {
        return Promise.reject(response.data);
      }
    } else {
      return Promise.reject(response.data);
    }
  },
  // 请求失败
  (error: any) => {
    console.log(error.response.status);
    if (error.response.status == 404) {
      // todo
    } else if (error.response.status == 403) {
      // todo
    }
    return Promise.reject(error);
  },
);

权限控制模块

权限控制使用了BaseAuthentication实现的,具体代码可参考authentication.py

from rest_framework import exceptions
from rest_framework.authentication import BaseAuthentication
from myapp.models import User

# 接口认证
class AdminTokenAuthtication(BaseAuthentication):
    def authenticate(self, request):
        adminToken = request.META.get("HTTP_ADMINTOKEN")

        print("检查adminToken==>" + adminToken)
        users = User.objects.filter(admin_token=adminToken)
        """
        判定条件:
            1. 传了adminToken 
            2. 查到了该帐号 
            3. 该帐号是管理员或演示帐号
        """
        if not adminToken or len(users) == 0 or users[0].role == '2':
            raise exceptions.AuthenticationFailed("AUTH_FAIL_END")
        else:
            print('adminToken验证通过')


路由模块实现

前端的路由是基于vue-router框架实现的,路由文件位于src的rooter的root.js文件中。预览如下:

  {
    path: '/admin',
    name: 'admin',
    redirect: '/admin/thing',
    component: () => import('/@/views/admin/main.vue'),
    children: [
      { path: 'thing', name: 'thing', component: () => import('/@/views/thing.vue') },
      { path: 'score', name: 'score', component: () => import('/@/views/score.vue') },
      { path: 'course', name: 'course', component: () => import('/@/views/course.vue') },
      { path: 'user', name: 'user', component: () => import('/@/views/user.vue') },
      { path: 'classification', name: 'classification', component: () => import('/@/views/classification.vue') },
      { path: 'tag', name: 'tag', component: () => import('/@/views/tag.vue') },
      { path: 'loginLog', name: 'loginLog', component: () => import('/@/views/login-log.vue') },
      { path: 'opLog', name: 'opLog', component: () => import('/@/views/op-log.vue') },
      { path: 'errorLog', name: 'errorLog', component: () => import('/@/views/error-log.vue') },
      { path: 'sysInfo', name: 'sysInfo', component: () => import('/@/views/sys-info.vue') },
    ]
  },

限速功能实现

限流(Throttle)就是限制客户端对API 的调用频率,是API开发者必须要考虑的因素。比如个别客户端(比如爬虫程序)短时间发起大量请求,超过了服务器能够处理的能力,将会影响其它用户的正常使用。又或者某个接口占用数据库资源比较多,如果同一时间该接口被大量调用,服务器可能会陷入僵死状态。为了保证API服务的稳定性,并防止接口受到恶意用户的攻击,我们必须要对我们的API服务进行限流。

我们使用了django的AnonRateThrottle限流类来实现的。可以参见myapp的auth目录下的MyRateThrottle.py文件

class MyRateThrottle(AnonRateThrottle):
    THROTTLE_RATES = {"anon": "2/min"}  # 限流每分钟只能请求2次

当某个api接口需要限流的时候,只需要添加注解即可,如下所示

@api_view(['POST'])
@throttle_classes([MyRateThrottle]) # 限流注解
def create(request):
    serializer = CommentSerializer(data=request.data)
    if serializer.is_valid():
        serializer.save()
        return APIResponse(code=0, msg='创建成功', data=serializer.data)
    else:
        print(serializer.errors)

    return APIResponse(code=1, msg='创建失败')

常见问题

  • 数据库版本有要求吗?

需要mysql 5.7以上

  • 前端 npm install 失败怎么办?

使用国内镜像安装,设置命令为:

npm config set registry https://registry.npm.taobao.org
  • 提示"演示账号无法操作",怎么办?

将用户的权限提高,修改b_user表的role字段

  • 如何更换后端请求地址

修改store文件夹下的constants.js文件中的BASE_URL,改成你自己的后端地址

  • 如何新增页面

在views文件夹下创建新的vue文件,写入界面代码,然后在router的root.js中添加路由即可。

标签:课程设计,varchar,title,python,request,源码,modal,const,data
From: https://blog.csdn.net/net19880504/article/details/137234717

相关文章

  • python golang 安装私有库的用法
    python安装在requirements.txt增加了github私有安装库的依赖git+https://${GITHUB_TOKEN}@github.com/YOLOTECHNOLOGY/bossjob-auth-lib.git@${GITHUB_LIB_BRANCH}当前该库无法通过pipenv直接安装,需要进入到虚拟环境,通过pipinstall进行安装安装前需要设置GITHUB_......
  • 基于springboot实现大学生入学审核系统项目【项目源码+论文说明】计算机毕业设计
    基于springboot实现大学生入学审核系统演示摘要随着信息技术在管理上越来越深入而广泛的应用,管理信息系统的实施在技术上已逐步成熟。本文介绍了大学生入学审核系统的开发全过程。通过分析大学生入学审核系统管理的不足,创建了一个计算机管理大学生入学审核系统的方案。文......
  • 基于springboot实现学生读书笔记共享平台系统项目【项目源码+论文说明】计算机毕业设
    基于springboot实现学生读书笔记共享平台系统演示摘要本论文主要论述了如何使用JAVA语言开发一个读书笔记共享平台,本系统将严格按照软件开发流程进行各个阶段的工作,采用B/S架构,面向对象编程思想进行项目开发。在引言中,作者将论述读书笔记共享平台的当前背景以及系统开发......
  • Python学习笔记
    Python一、导学Pthon语言基础入门 Pthon语言高阶加强大数据分析PySpark二、初识Python你好Python程序print("HelloWorld!!")>>HelloWorld!!!输入的双引号和括号为英文字符三、Python基础语法(一)字面量     1.定义:在代码中,被写下来的固定的值,称之为字......
  • Python简直是万能的,这5大主要用途你一定要知道!
    众所周知小学生的教材里面已经有Python了,国家二级计算机证也需要学习Python了!因为Python简单、入门快,是不少程序员入门的首选语言。如果你想学Python,或者你刚开始学习Python,那么你可能会问:“我能用Python做什么?”这个问题不好回答,因为Python有很多用途。但是随着时间,我......
  • 熬夜梳理!Python 技术大佬总结了53个使用技巧!
    大家好,今天给大家分享老司机总结的一些干货技巧,非常有价值,尤其是对Python编程基础比较薄弱的小伙伴,记得收藏哦,欢迎关注、点赞。交流群建立Python交流群,想要进技术交流群的同学,可以直接加微信号:dkl88191。加的时候备注一下:研究方向+学校/公司+知乎,即可。然后就可以拉你......
  • WingPro for Mac(Python开发工具)
    了解更多相关内容请点击此处WingProforMac是一款Python开发环境,适用于Mac平台。它由Wingware公司开发,提供了一系列强工具,可以帮助Python开发人员轻松地编写、调试和测试Python代码。WingProforMac具有一个直观的用户界面,支持多种编程语言和框架,例如Django、Flask、Pyramid......
  • 【Python基础】判断语句
    文章目录@[toc]布尔类型示例比较运算符逻辑运算符and示例or示例not示例特殊情况下的逻辑运算符andorif判断语句格式示例else判断语句格式示例elif语句格式执行流程示例if嵌套格式示例个人主页:丷从心.系列专栏:Python基础学习指南:Python学习指南布尔......
  • 【附源码】JAVA计算机毕业设计智慧外贸平台(springboot+mysql+开题+论文)
    本系统(程序+源码)带文档lw万字以上 文末可获取一份本项目的java源码和数据库参考。系统程序文件列表开题报告内容研究背景在全球经济一体化的时代背景下,智慧外贸平台的建设已成为提升我国外贸竞争力、优化国际贸易环境的关键一环。随着信息技术的迅猛发展,传统的外贸模式已......
  • 【附源码】JAVA计算机毕业设计智慧物流管理系统(springboot+mysql+开题+论文)
    本系统(程序+源码)带文档lw万字以上 文末可获取一份本项目的java源码和数据库参考。系统程序文件列表开题报告内容研究背景随着信息化时代的快速发展,物流行业作为现代经济体系的重要支柱,正面临着前所未有的机遇与挑战。传统的物流管理方式已难以适应现代社会的需求,智能化、......