当前位置: 首页 > news >正文

python大学社团管理系统开发文档

项目介绍

一直想做一款大学社团管理系统,看了很多优秀的开源项目但是发现没有合适的。于是利用空闲休息时间开始自己写了一套管理系统。

在线体验

  • 代码下载:https://github.com/geeeeeeeek/python_team
  • 演示地址:http://team.gitapp.cn/

(账号:admin123 密码:admin123)

功能介绍

系统分为后台和前台两部分。

后台的主要功能:

  • 社团管理:管理系统可以录入、修改和查询社团的基本信息,如社团名称、地址、备注等。
  • 类型管理:系统可以管理社团的类型信息,包括类型的名称等。
  • 标签管理:管理标签录入、修改和查询标签的信息。
  • 评论管理:管理和浏览整个网站的评论信息。
  • 成员管理:管理和浏览社团的成员信息。
  • 用户管理:管理和浏览网站的用户信息,可以新增、编辑和删除用户。
  • 统计分析:系统可以根据社团的活动数据和会员参与度进行统计和分析,帮助管理员了解整个系统的状况。
  • 消息管理:社团管理员可以在系统上发布消息,整个网站的用户都能收到。
  • 系统信息:管理员可以查看系统的基本信息,包括系统名称、服务器信息、内存信息、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_team 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)
  • 分类(classification)
  • 标签(tag)
  • 用户(user)
  • 评价(comment)
  • 日志(log)
  • 通知(notice)

关系如下:

  • 一个社团有一个分类
  • 一个分类可以对应多个社团
  • 一个社团有多个标签
  • 一个标签可以对应多个社团

数据表设计如下:


// 社团
Table thing {thing_id int [pk]classification_id int [ref: > C.classification_id]tag_id int [ref: <> tag.tag_id]title varcharcover varchar  // 封面zongzhi varchar // 社团宗旨price varchar // 价格status int // 上线0 下架1description textmobile varchar email varchar // 邮箱location varchar  // 社团地址create_time datetimepv intwish_count intrecommend_count intwish int [ref: <> user.user_id]collect int [ref: <> user.user_id]}// 分类管理Table classification as C {classification_id int [pk]pid inttitle varcharcreate_time datetime}Table tag {tag_id int [pk]title varcharcreate_time datetime}// 评论Table comment {comment_id int [pk]content varcharuser_id int [ref: > user.user_id]thing_id int [ref: > thing.thing_id]comment_time datetimelike_count int}// 用户Table user {user_id int [pk]role varchar // 1管理员 2普通用户 3演示帐号status int // 0正常 1封号username varcharpassword varcharnickname varcharavatar varchardescription varcharwish int [ref: <> thing.thing_id]email varcharmobile varcharscore int // 积分push_email varchar // 推送邮箱push_switch int  // 推送开关token varcharadmin_token varchar} Table record {record_id int [pk]user_id int [ref: > user.user_id]classification_id int [ref: > C.classification_id]thing_id int [ref: > thing.thing_id]title varcharrecord_time varchar}Table login_log {log_id int [pk]username varcharip varcharlog_time datetime}// 操作日志Table op_log {id int [pk]re_ip varcharre_time datetimere_url varcharre_method varcharre_content varcharaccess_time varchar}// 异常日志Table error_log {id int [pk]ip varcharmethod varcharcontent varcharlog_time varchar}// 成员管理Table order {order_id int [pk]user_id int [ref: > user.user_id]thing_id int [ref: > thing.thing_id]status varchar // 1未审核 2通过 7取消加入create_time datetime // 申请时间receiver_name varchar // 姓名receiver_address varchar // 地址receiver_phone varchar // 电话remark varchar // 备注}// 通知Table notice {id int [pk]content varcharcreate_time datetime}

开发过程

无论是社团管理、用户管理、标签管理、分类管理、评价管理、日志管理、消息管理等功能都是基于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 = Thingfields = '__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-tablesize="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"><div><a-form ref="myform" :label-col="{ style: { width: '80px' } }" :model="modal.form" :rules="modal.rules"><a-row :gutter="24"><a-col span="24"><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-selectplaceholder="请选择"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="标签"><a-select mode="multiple" placeholder="请选择" allowClear v-model:value="modal.form.tag"><template v-for="item in modal.tagData"><a-select-option :value="item.id">{{ item.title }}</a-select-option></template></a-select></a-form-item></a-col><a-col span="24"><a-form-item label="封面"><a-upload-draggername="file"accept="image/*":multiple="false":before-upload="beforeUpload"v-model:file-list="fileList"><p class="ant-upload-drag-icon"><template v-if="modal.form.coverUrl"><img :src="modal.form.coverUrl" style="width: 60px; height: 80px" /></template><template v-else><file-image-outlined /></template></p><p class="ant-upload-text"> 请选择要上传的图片 </p></a-upload-dragger></a-form-item></a-col><a-col span="24"><a-form-item label="社团简介"><a-textarea placeholder="请输入" v-model:value="modal.form.description" /></a-form-item></a-col><a-col span="12"><a-form-item label="社团宗旨"><a-input placeholder="请输入" v-model:value="modal.form.zongzhi" style="width: 100%" /></a-form-item></a-col><a-col span="12"><a-form-item label="手机号"><a-input-number placeholder="请输入" :min="0" v-model:value="modal.form.mobile" style="width: 100%" /></a-form-item></a-col><a-col span="12"><a-form-item label="社团邮箱"><a-input placeholder="请输入" v-model:value="modal.form.email" style="width: 100%" /></a-form-item></a-col><a-col span="12"><a-form-item label="社团地址"><a-input placeholder="请输入" v-model:value="modal.form.location" style="width: 100%" /></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/admin/thing';import { listApi as listClassificationApi } from '/@/api/admin/classification';import { listApi as listTagApi } from '/@/api/admin/tag';import { BASE_URL } from '/@/store/constants';import { FileImageOutlined } from '@ant-design/icons-vue';const columns = reactive([{title: '序号',dataIndex: 'index',key: 'index',width: 60,},{title: '名称',dataIndex: 'title',key: 'title',},{title: '社团宗旨',dataIndex: 'zongzhi',key: 'zongzhi',},{title: '社团邮箱',dataIndex: 'email',key: 'email',},{title: '手机号',dataIndex: 'mobile',key: 'mobile',},{title: '社团地址',dataIndex: 'location',key: 'location',},{title: '简介',dataIndex: 'description',key: 'description',customRender: ({ text, record, index, column }) => (text ? text.substring(0, 10) + '...' : '--'),},{title: '状态',dataIndex: 'status',key: 'status',customRender: ({ text, record, index, column }) => (text === '0' ? '在营' : '暂停'),},{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 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,tag: [],zongzhi: undefined,mobile: undefined,email: undefined,location: undefined,status: undefined,cover: undefined,coverUrl: undefined,imageFile: undefined,},rules: {title: [{ required: true, message: '请输入名称', trigger: 'change' }],classification: [{ required: true, message: '请选择分类', trigger: 'change' }],price: [{ 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;item.price = item.price;});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);if (modal.form.classification) {formData.append('classification', modal.form.classification);}if (modal.form.tag) {modal.form.tag.forEach(function (value) {if (value) {formData.append('tag', value);}});}if (modal.form.imageFile) {formData.append('cover', modal.form.imageFile);}formData.append('description', modal.form.description || '');formData.append('price', modal.form.price || '');if (modal.form.mobile) {formData.append('mobile', modal.form.mobile);}if (modal.form.zongzhi) {formData.append('zongzhi', modal.form.zongzhi);}if (modal.form.email) {formData.append('email', modal.form.email);}if (modal.form.location) {formData.append('location', modal.form.location);}if (modal.form.status) {formData.append('status', modal.form.status);}if (modal.editFlag) {updateApi({id: modal.form.id,},formData,).then((res) => {hideModal();getDataList();}).catch((err) => {console.log(err);message.error(err.msg || '操作失败');});} else {createApi(formData).then((res) => {hideModal();getDataList();}).catch((err) => {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: 'overview', name: 'overview', component: () => import('/@/views/admin/overview.vue') },{ path: 'order', name: 'order', component: () => import('/@/views/admin/order.vue') },{ path: 'thing', name: 'thing', component: () => import('/@/views/admin/thing.vue') },{ path: 'comment', name: 'comment', component: () => import('/@/views/admin/comment.vue') },{ path: 'user', name: 'user', component: () => import('/@/views/admin/user.vue') },{ path: 'classification', name: 'classification', component: () => import('/@/views/admin/classification.vue') },{ path: 'tag', name: 'tag', component: () => import('/@/views/admin/tag.vue') },{ path: 'ad', name: 'ad', component: () => import('/@/views/admin/ad.vue') },{ path: 'notice', name: 'notice', component: () => import('/@/views/admin/notice.vue') },{ path: 'loginLog', name: 'loginLog', component: () => import('/@/views/admin/login-log.vue') },{ path: 'opLog', name: 'opLog', component: () => import('/@/views/admin/op-log.vue') },{ path: 'errorLog', name: 'errorLog', component: () => import('/@/views/admin/error-log.vue') },{ path: 'sysInfo', name: 'sysInfo', component: () => import('/@/views/admin/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中添加路由即可。

相关文章:

python大学社团管理系统开发文档

项目介绍 一直想做一款大学社团管理系统&#xff0c;看了很多优秀的开源项目但是发现没有合适的。于是利用空闲休息时间开始自己写了一套管理系统。 在线体验 代码下载&#xff1a;https://github.com/geeeeeeeek/python_team演示地址&#xff1a;http://team.gitapp.cn/ &…...

leetcode 1328.破坏回文串

题目链接LeetCode1328 1.题目 给你一个由小写英文字母组成的回文字符串 palindrome &#xff0c;请你将其中 一个 字符用任意小写英文字母替换&#xff0c;使得结果字符串的 字典序最小 &#xff0c;且 不是 回文串。 请你返回结果字符串。如果无法做到&#xff0c;则返回一个…...

重学SpringBoot3-自动配置机制

重学SpringBoot3-自动配置机制 引言Spring Boot 自动配置原理示例&#xff1a;Spring Boot Web 自动配置深入理解总结相关阅读 引言 Spring Boot 的自动配置是其最强大的特性之一&#xff0c;它允许开发者通过最少的配置实现应用程序的快速开发和部署。这一切都得益于 Spring …...

sql基本语法+实验实践

sql语法 注释&#xff1a; 单行 --注释内容# 注释内容多行 /* 注释内容 */数据定义语言DDL 查询所有数据库 show databases;注意是databases而不是database。 查询当前数据库 select database();创建数据库 create database [if not exists] 数据库名 [default charset 字符…...

Node.js中的并发和多线程处理

在Node.js中&#xff0c;处理并发和多线程是一个非常重要的话题。由于Node.js是单线程的&#xff0c;这意味着它在任何给定时间内只能执行一个任务。然而&#xff0c;Node.js的事件驱动和非阻塞I/O模型使得处理并发和多线程变得更加高效和简单。在本文中&#xff0c;我们将探讨…...

node.js 封装分页查询

node.js封装sql分页查询 方法&#xff1a; /*** 生成分页查询sql* param {string} table 表名* param {number} pageNum 分页页数 * param {number} pageSize 分页条数 * param {object} query 查询对象 例&#xff1a;{id:1,name:小明}* returns sql语句*/ const limit (ta…...

iptables 基本使用

iptables 主要用到两个表&#xff1a;filter 和 nat&#xff0c;其中 filter 表可以用来过滤数据包&#xff1b;nat 可以用来修改数据包的源地址和目的地址。 chain chain 是 table 中对数据包进行匹配的规则&#xff0c;对于 filter 来说 chain 有 INPUT & OUTPUT & …...

食品笔记()

吃东西有时不注意&#xff0c;就容易不舒服&#xff0c;记录下。 辣椒 辣椒真是个让人又爱又恨的东西。 看着想吃&#xff0c;吃着过瘾&#xff0c;吃完容易肚子疼。 主要是这东西本身就会刺激身体&#xff0c;即使是能吃辣的人&#xff0c;也容易造成肠胃发炎。 适量吃些即…...

C++入门和基础

目录 文章目录 前言 一、C关键字 二、命名空间 2.1 命名空间的定义 2.2 命名空间的使用 2.3 标准命名空间 三、C输入&输出 四、缺省参数 4.1 缺省参数的概念 4.2 缺省参数的分类 五、函数重载 5.1 函数重载的简介 5.2 函数重载的分类 六、引用 6.1 引用的…...

一些C语言知识

C语言的内置类型&#xff1a; char short int long float double C99中引入了bool类型&#xff0c;用来表示真假的变量类型&#xff0c;包含true&#xff0c;false。 这个代码的执行结果是什么&#xff1f;好好想想哦&#xff0c;坑挺多的。 #include <stdio.h>int mai…...

代码工具APEX的入门使用(未包含安装)

第一次使用APEX是2019年&#xff0c;这个技术成名已久只是我了解的比较晚。请看Oracle ACE的网站&#xff0c;这就是用APEX做的。实际上有一次我看O记的人操作他们的办公流程&#xff0c;都是用APEX做的。 那一年&#xff0c;我用APEX做了一个CMDB的管理系统。那时候还没有流行…...

负载均衡.

简介: 将请求/数据【均匀】分摊到多个操作单元上执行&#xff0c;负载均衡的关键在于【均匀】。 负载均衡的分类: 网络通信分类 四层负载均衡:基于 IP 地址和端口进行请求的转发。七层负载均衡:根据访问用户的 HTTP 请求头、URL 信息将请求转发到特定的主机。 载体维度分类 硬…...

Git 指令深入浅出【2】—— 分支管理

Git 指令深入浅出【2】—— 分支管理 分支管理1. 常用分支管理指令2. 合并分支合并冲突合并模式 3. 实战演习 分支管理 1. 常用分支管理指令 # 查看本地分支 git branch# 查看远程分支 git branch -r# 查看全部分支 git branch -aHEAD 指向的才是当前的工作分支 # 查看当前分…...

工作流/任务卸载相关开源论文分享

decima-sim 概述&#xff1a; 图神经网络强化学习处理多工作流 用的spark的仿真环境&#xff0c;mit的论文&#xff0c;价值很高&#xff0c;高被引&#xff1a;663仓库地址&#xff1a;https://github.com/hongzimao/decima-sim论文&#xff1a;https://web.mit.edu/decima/co…...

为什么要用Python?

为什么要用Python&#xff1f; Python简单易用&#xff1a;提供大量的简单易用数据结构和内置库&#xff0c;语法结构也很简单易读&#xff0c;不需要使用括号来进行代码块分组&#xff0c;也不需要预声明变量或参数。Python开发效率高&#xff1a;简单易用的前提下&#xff0…...

北京大学发布,将试错引入大模型代理学习!

引言&#xff1a;探索语言智能的新边界 在人工智能的发展历程中&#xff0c;语言智能始终是一个核心的研究领域。随着大语言模型&#xff08;LLM&#xff09;的兴起&#xff0c;我们对语言智能的理解和应用已经迈入了一个新的阶段。这些模型不仅能够理解和生成自然语言&#x…...

Java 设计模式

编程设计模式六大原则 开闭原则&#xff08;Open Close Principle&#xff09;&#xff1a;对扩展开放&#xff0c;对修改关闭。在程序需要进行拓展的时候&#xff0c;不能去修改原有的代码&#xff0c;实现一个热插拔的效果。简言之&#xff0c;是为了使程序的扩展性好&#…...

Kivy和BeeWare 开发APP的优缺点,及其发展历史

Kivy和BeeWare都是流行的Python框架&#xff0c;用于开发移动应用。它们各自有独特的特点和优势&#xff0c;同时也面临一些挑战和限制。下面是对这两个框架的开发优缺点及其发展历史的总结。 Kivy 发展历史 起源&#xff1a;Kivy诞生于2010年&#xff0c;旨在提供一个用于P…...

C++递推

统计每个月兔子的总数 #include<bits/stdc.h> using namespace std; int n,sum0; void f(int); int main() {int a[1000];cin>>n;a[1]1;a[2]2;for(int i3;i<1000;i){a[i]a[i-1]a[i-2];}cout<<a[n];return 0; } void f(int n){}猴子吃桃子 #include<b…...

C++ 面试题

一、基础语法 1. C 和 C的区别 i. C是面向对象的的编程语言&#xff0c;C是面向过程的编程语言 ii. C中的内存分配运算符是new/delete而C 中是malloc和free iii. C中有函数重载而C 中没有 iv. C中新增了引用的概念而C 中只有值和指针 2. struct 和 class的区别 i. struc…...

React Native 开发环境搭建(全平台详解)

React Native 开发环境搭建&#xff08;全平台详解&#xff09; 在开始使用 React Native 开发移动应用之前&#xff0c;正确设置开发环境是至关重要的一步。本文将为你提供一份全面的指南&#xff0c;涵盖 macOS 和 Windows 平台的配置步骤&#xff0c;如何在 Android 和 iOS…...

【力扣数据库知识手册笔记】索引

索引 索引的优缺点 优点1. 通过创建唯一性索引&#xff0c;可以保证数据库表中每一行数据的唯一性。2. 可以加快数据的检索速度&#xff08;创建索引的主要原因&#xff09;。3. 可以加速表和表之间的连接&#xff0c;实现数据的参考完整性。4. 可以在查询过程中&#xff0c;…...

练习(含atoi的模拟实现,自定义类型等练习)

一、结构体大小的计算及位段 &#xff08;结构体大小计算及位段 详解请看&#xff1a;自定义类型&#xff1a;结构体进阶-CSDN博客&#xff09; 1.在32位系统环境&#xff0c;编译选项为4字节对齐&#xff0c;那么sizeof(A)和sizeof(B)是多少&#xff1f; #pragma pack(4)st…...

【机器视觉】单目测距——运动结构恢复

ps&#xff1a;图是随便找的&#xff0c;为了凑个封面 前言 在前面对光流法进行进一步改进&#xff0c;希望将2D光流推广至3D场景流时&#xff0c;发现2D转3D过程中存在尺度歧义问题&#xff0c;需要补全摄像头拍摄图像中缺失的深度信息&#xff0c;否则解空间不收敛&#xf…...

vue3 字体颜色设置的多种方式

在Vue 3中设置字体颜色可以通过多种方式实现&#xff0c;这取决于你是想在组件内部直接设置&#xff0c;还是在CSS/SCSS/LESS等样式文件中定义。以下是几种常见的方法&#xff1a; 1. 内联样式 你可以直接在模板中使用style绑定来设置字体颜色。 <template><div :s…...

ESP32 I2S音频总线学习笔记(四): INMP441采集音频并实时播放

简介 前面两期文章我们介绍了I2S的读取和写入&#xff0c;一个是通过INMP441麦克风模块采集音频&#xff0c;一个是通过PCM5102A模块播放音频&#xff0c;那如果我们将两者结合起来&#xff0c;将麦克风采集到的音频通过PCM5102A播放&#xff0c;是不是就可以做一个扩音器了呢…...

Nuxt.js 中的路由配置详解

Nuxt.js 通过其内置的路由系统简化了应用的路由配置&#xff0c;使得开发者可以轻松地管理页面导航和 URL 结构。路由配置主要涉及页面组件的组织、动态路由的设置以及路由元信息的配置。 自动路由生成 Nuxt.js 会根据 pages 目录下的文件结构自动生成路由配置。每个文件都会对…...

04-初识css

一、css样式引入 1.1.内部样式 <div style"width: 100px;"></div>1.2.外部样式 1.2.1.外部样式1 <style>.aa {width: 100px;} </style> <div class"aa"></div>1.2.2.外部样式2 <!-- rel内表面引入的是style样…...

C++ 求圆面积的程序(Program to find area of a circle)

给定半径r&#xff0c;求圆的面积。圆的面积应精确到小数点后5位。 例子&#xff1a; 输入&#xff1a;r 5 输出&#xff1a;78.53982 解释&#xff1a;由于面积 PI * r * r 3.14159265358979323846 * 5 * 5 78.53982&#xff0c;因为我们只保留小数点后 5 位数字。 输…...

数据库分批入库

今天在工作中&#xff0c;遇到一个问题&#xff0c;就是分批查询的时候&#xff0c;由于批次过大导致出现了一些问题&#xff0c;一下是问题描述和解决方案&#xff1a; 示例&#xff1a; // 假设已有数据列表 dataList 和 PreparedStatement pstmt int batchSize 1000; // …...