Flask构建的校园请假审批系统:SQLite本地数据库+五角色权限前端页面+完整Python后端

该文章已生成可运行项目,

本文还有配套的精品资源,点击获取 menu-r.4af5f7ec.gif

简介:基于Flask框架开发的轻量级校园请假审批系统,直接运行即可使用。后端用SQLite存储用户资料、请假申请、审批状态和角色权限数据,无需额外安装数据库服务。系统预设管理员、学生、教师、辅导员、教务员五类角色,各自拥有独立首页(admin.html、student.html等)和操作权限,通过role.py统一管理角色逻辑,session.html处理登录态与身份识别。核心功能包括请假提交、多级审批流程、考勤记录查看(attendance.html)、密码生成工具(generate_password.py)及数据库初始化脚本(data_creator.py)。前端采用Bootstrap 5响应式布局,集成jQuery、BootstrapTable实现动态表格展示、BootstrapFileinput支持附件上传、jQuery-Confirm提供交互提示,所有页面继承base.html保持风格统一。静态资源包含7个CSS样式文件、10个JS交互脚本、FontAwesome图标库及favicon.ico。配套提供datadb参考手册.pdf,详细说明data.db中各表字段含义与关联关系,便于二次开发或字段扩展。启动方式简单:安装requirements.txt依赖后运行launch.py或app.py即可访问本地服务。

1. 项目概述:为什么一个校园请假系统值得从零重做一遍?

我带过三届计算机专业的毕业设计,每年都有至少五个学生选“教务管理系统”或“学生事务平台”这类题目。但翻看他们的代码,八成卡在权限模型上——不是学生能删老师账号,就是辅导员点了审批按钮却没更新状态,更别提附件上传失败、表格加载空白、登录后跳转错页面这些“经典现场”。直到去年帮学院信息办快速搭一套临时请假流程,我才真正意识到:一个能跑通的轻量级系统,比十个PPT架构图更有说服力。这套Flask请假系统,就是我在真实场景里反复打磨出来的“最小可行产品”——它不追求高并发、不对接教务API、不搞微服务拆分,就专注把“学生填表→辅导员初审→教务员终审→状态同步→考勤归档”这条主链路,用最朴素的方式走通、压稳、留痕。

核心关键词“Flask请假系统”“SQLite审批系统”“多角色权限管理”,其实对应三个现实痛点:第一,“Flask请假系统”意味着它必须足够轻——没有Docker编排、没有Nginx反向代理、没有Redis缓存,一台4G内存的旧笔记本装个Python 3.8就能跑;第二,“SQLite审批系统”直指部署门槛——不用配MySQL用户权限、不用开3306端口、不用处理数据库连接池泄漏,整个数据就躺在data.db这个单文件里,双击备份、拖拽迁移、文本编辑器直接查字段;第三,“多角色权限管理”不是简单if-else判断role==’admin’,而是把“谁能看到什么按钮”“谁能在哪个页面提交什么表单”“谁修改的数据会被日志记录”这三层逻辑,揉进路由装饰器、模板上下文、数据库查询条件里,让权限真正长在业务毛细血管中。它适合两类人:一是刚学完Flask基础想动手做项目的大学生,代码结构清晰到能当教材用;二是学院IT老师需要一周内上线临时流程的实战者,所有依赖都在requirements.txt里列得明明白白,launch.py点一下就起服务。你不需要懂JWT鉴权原理,但能看清session如何绑定用户ID;你不必研究SQLAlchemy ORM高级特性,但能读懂data_creator.py里那几条CREATE TABLE语句怎么支撑起五角色协作。这就是它的价值:把抽象的“权限控制”还原成可触摸的HTML按钮、可调试的Python装饰器、可验证的SQLite记录

2. 整体架构与设计思路:为什么选择SQLite+Flask而非Django或Vue?

2.1 技术栈取舍背后的现实考量

很多人看到“五角色权限”第一反应是上Django Admin——毕竟自带用户管理、权限系统、后台界面。但我坚持用原生Flask,原因很实在:Django的约定大于配置,在教学场景里反而成了障碍。学生要改一个审批状态按钮的位置,得先搞懂template inheritance的loader顺序、staticfiles的collectstatic机制、middleware的执行链;而Flask里,你打开student.html,找到那个<button onclick="submitLeave()">,直接加个{% if current_user.role == 'teacher' %}包裹就行。这种“所见即所得”的调试体验,对新手建立信心至关重要。

至于数据库选SQLite而非MySQL或PostgreSQL,更是被现实逼出来的选择。去年帮某高职院校部署时,对方机房连远程桌面都卡顿,更别说让非专业老师去配MySQL root密码、开防火墙端口、处理socket连接超时。而SQLite呢?data.db就是一个普通文件,os.path.exists('data.db')一行代码就能判断数据库是否存在,sqlite3.connect('data.db')自动创建——没有服务进程、没有配置文件、没有root账户,连备份都只需要复制这个文件。当然,它有硬伤:不支持高并发写入、没有行级锁、大数据量下查询变慢。但校园请假场景是什么量级?全校5000学生,按每月平均每人请1次假算,一年才6万条记录,SQLite单表轻松扛住。真到了需要分库分表那天,把data_creator.py里的SQL语句导出,再用Navicat一键迁移到MySQL,成本远低于初期就为“可能的扩展性”牺牲掉的开发效率。

前端放弃Vue/React而用Bootstrap 5,同样基于交付确定性。Vue项目要配webpack、处理跨域、调试v-model双向绑定失效;而Bootstrap 5的class命名直白如btn btn-primary disabled,jQuery操作DOM就像写伪代码:$('#leave-table').bootstrapTable('refresh')。更重要的是,所有角色首页(admin.html/student.html等)都继承自base.html,这个母版里已经预置了导航栏角色判断逻辑:

<!-- base.html 片段 -->
<nav class="navbar">
  {% if current_user.role == 'admin' %}
    <a href="{{ url_for('admin_dashboard') }}">管理员面板</a>
  {% elif current_user.role == 'student' %}
    <a href="{{ url_for('student_leave_form') }}">我要请假</a>
  {% endif %}
</nav>

这种“模板即权限”的设计,让前端同学不用理解RBAC模型,只要记住“不同角色渲染不同链接”就行。而BootstrapTable集成的服务器分页、列排序、搜索框,全是开箱即用的data属性驱动,比如attendance.html里这行:

<table data-toggle="table" 
       data-url="{{ url_for('get_attendance_data') }}"
       data-pagination="true"
       data-search="true">
  <thead>
    <tr>
      <th data-field="student_id">学号</th>
      <th data-field="status">考勤状态</th>
    </tr>
  </thead>
</table>

后端app.py里对应的/api/attendance路由,只需返回标准JSON数组,表格自动渲染。这种“约定优于配置”的默契,正是轻量级系统的生命线。

2.2 五角色权限模型的落地实现

真正的难点不在技术选型,而在如何把“管理员、教师、辅导员、学生、教务员”这五个抽象名词,转化成代码里可验证、可审计、可追溯的行为边界。很多项目把权限写死在模板里,结果出现学生能访问/admin/users路由的漏洞。本系统采用三层防御:

第一层:路由级拦截(app.py中的装饰器)
定义@require_role('admin')装饰器,所有敏感路由必须声明所需角色:

def require_role(role):
    def decorator(f):
        @wraps(f)
        def decorated_function(*args, **kwargs):
            if not session.get('user_id'):
                return redirect(url_for('login'))
            user = User.query.get(session['user_id'])
            if user.role != role:
                flash('权限不足', 'error')
                return redirect(url_for('index'))
            return f(*args, **kwargs)
        return decorated_function
    return decorator

@app.route('/admin/manage_users')
@require_role('admin')
def manage_users():
    return render_template('admin.html')

这里的关键细节是:装饰器必须在@app.route之后、函数定义之前,否则Flask无法注册路由。我试过把@require_role放在外面,结果所有路由都404——这是新手踩坑最多的地方。

第二层:模板级过滤(Jinja2条件渲染)
即使路由被误放行,模板里也设第二道关卡。比如在student.html中,只有学生本人能修改自己的请假申请:

<!-- 学生只能编辑自己未审批的申请 -->
{% if leave.status == 'pending' and leave.student_id == current_user.id %}
  <button onclick="editLeave({{ leave.id }})">编辑</button>
{% endif %}

第三层:数据级隔离(查询时强制添加WHERE条件)
最危险的是列表页,比如/teacher/approvals要显示该教师负责班级的学生请假单。如果只靠前端隐藏按钮,恶意用户改URL就能看到全部数据。因此在teacher.py中,所有查询都强制绑定教师ID:

# teacher.py
def get_pending_leaves_for_teacher(teacher_id):
    # 关键:JOIN班级表,确保只查该教师授课班级的学生
    return db.session.query(Leave).join(Student).join(Class).filter(
        Class.teacher_id == teacher_id,
        Leave.status == 'pending'
    ).all()

这三层像三把锁:路由锁防止非法入口,模板锁防止UI越权,数据锁杜绝SQL注入式绕过。配套的datadb参考手册.pdf里,专门用ER图标注了teacher_id字段如何贯穿classes→students→leaves三张表,让二次开发者一眼看清权限数据链路。

3. 核心模块解析与实操要点

3.1 数据库设计:SQLite如何支撑五角色协作?

data.db虽小,却是整个系统的地基。datadb参考手册.pdf里详细列出了7张表,但真正驱动业务的是这四张核心表:

表名关键字段作用说明设计巧思
usersid, username, password_hash, role, real_name, dept_id所有角色统一存储role字段用VARCHAR而非ENUM,方便后期扩展新角色(如“实习指导教师”)
leavesid, student_id, reason, start_date, end_date, status, attachment_path, created_at请假主记录status用TEXT存’pending’,’approved’,’rejected’,避免INT枚举导致的维护成本
approvalsid, leave_id, approver_id, status, comment, approved_at审批流水日志单独建表而非在leaves加approval_id字段,支持多级审批(辅导员初审+教务员终审)
departmentsid, name, type部门分类(教学部/学工部/教务处)type字段区分部门性质,用于动态生成角色首页的待办统计

特别要注意leaves.attachment_path字段的设计。很多项目直接存文件二进制到BLOB,结果数据库暴涨且备份困难。本系统采用“文件系统存储+数据库存路径”策略:上传时用secure_filename()清洗文件名,保存到static/uploads/目录,数据库只记相对路径uploads/20240515_abc123.jpg。这样做的好处是:备份时data.db保持小巧,清理附件只需删static/uploads目录;排查问题时,直接打开路径就能看到原始文件。

初始化脚本data_creator.py的实操要点在于事务控制与默认数据注入。SQLite默认不开启外键约束,必须显式启用:

# data_creator.py 关键片段
conn = sqlite3.connect('data.db')
conn.execute("PRAGMA foreign_keys = ON")  # 必须开启外键!
cursor = conn.cursor()

# 创建表语句(省略)
cursor.execute("""
    CREATE TABLE IF NOT EXISTS users (
        id INTEGER PRIMARY KEY AUTOINCREMENT,
        username TEXT UNIQUE NOT NULL,
        password_hash TEXT NOT NULL,
        role TEXT NOT NULL,
        real_name TEXT,
        dept_id INTEGER,
        FOREIGN KEY (dept_id) REFERENCES departments(id)
    )
""")

# 插入默认管理员(密码用generate_password.py生成)
cursor.execute(
    "INSERT OR IGNORE INTO users (username, password_hash, role, real_name) VALUES (?, ?, ?, ?)",
    ('admin', 'pbkdf2:sha256:260000$...', 'admin', '系统管理员')
)
conn.commit()  # 必须commit,否则数据不持久化

这里有两个易错点:一是忘记PRAGMA foreign_keys = ON,导致外键约束形同虚设;二是插入默认数据用INSERT OR IGNORE而非INSERT,避免重复运行脚本时报“UNIQUE constraint failed”错误。generate_password.py工具则用werkzeug.security.generate_password_hash()生成符合Flask安全标准的哈希值,比手写hashlib.sha256().hexdigest()更可靠——后者缺少盐值(salt)和迭代次数,容易被彩虹表破解。

3.2 角色权限管理:role.py如何让权限逻辑可维护?

role.py不是简单的角色常量定义,而是权限规则的中央调度器。它解决三个关键问题:角色能力映射、页面可见性控制、操作可行性判断

首先,定义角色能力矩阵(role_capabilities.py):

# role_capabilities.py
ROLE_CAPABILITIES = {
    'admin': {
        'can_manage_users': True,
        'can_view_all_leaves': True,
        'can_export_data': True,
        'can_edit_system_config': True
    },
    'teacher': {
        'can_view_class_attendance': True,
        'can_approve_leaves': True,
        'can_upload_teaching_materials': False  # 教师无材料上传权限
    },
    # 其他角色...
}

然后在role.py中封装校验方法:

# role.py
from flask import session, abort
from app.classes.user import User

def has_capability(capability):
    """检查当前用户是否具备某项能力"""
    if 'user_id' not in session:
        return False
    user = User.query.get(session['user_id'])
    if not user:
        return False
    caps = ROLE_CAPABILITIES.get(user.role, {})
    return caps.get(capability, False)

def require_capability(capability):
    """装饰器:要求用户具备某项能力"""
    def decorator(f):
        @wraps(f)
        def decorated_function(*args, **kwargs):
            if not has_capability(capability):
                abort(403)  # 返回HTTP 403 Forbidden
            return f(*args, **kwargs)
        return decorated_function
    return decorator

这个设计的优势在于:权限变更无需改路由或模板,只需修改ROLE_CAPABILITIES字典。比如教务处要求教师也能查看全校考勤汇总,只需把'teacher'下的'can_view_all_attendance'设为True,所有调用has_capability('can_view_all_attendance')的地方自动生效。

在模板中使用更直观:

<!-- admin.html -->
{% if has_capability('can_manage_users') %}
  <a href="{{ url_for('admin_manage_users') }}" class="btn btn-warning">用户管理</a>
{% endif %}

<!-- teacher.html -->
{% if has_capability('can_approve_leaves') %}
  <button onclick="approveLeave({{ leave.id }})" class="btn btn-success">批准</button>
{% endif %}

注意这里调用的是has_capability()函数而非Jinja2变量,因为能力判断需实时查询数据库(如教师是否被分配到某班级),不能静态渲染。为此,app.pybefore_request钩子中将has_capability注入模板上下文:

@app.before_request
def inject_capabilities():
    g.has_capability = has_capability  # g对象全局可用

这样模板里就能直接用has_capability('xxx'),无需每次传参。这种“能力即函数”的设计,比硬编码if current_user.role in ['admin', 'office']清晰得多,也便于后期接入LDAP或OAuth2认证时替换能力校验逻辑。

3.3 前端交互关键组件:BootstrapTable与文件上传的避坑指南

前端最常出问题的两个组件是BootstrapTable动态加载和BootstrapFileinput文件上传。它们的坑不在文档里,而在浏览器兼容性和Flask请求处理的细节中。

BootstrapTable分页失效问题
现象:点击第二页,表格内容不变。根源在于Flask路由返回的JSON格式不符合BootstrapTable预期。正确响应必须包含totalrows两个顶层字段:

# app.py 正确写法
@app.route('/api/leaves')
def api_leaves():
    page = request.args.get('page', 1, type=int)
    limit = request.args.get('limit', 10, type=int)

    # 查询总记录数(用于计算总页数)
    total = Leave.query.count()

    # 查询当前页数据
    leaves = Leave.query.offset((page-1)*limit).limit(limit).all()

    # 必须返回 { "total": 100, "rows": [...] } 结构
    return jsonify({
        'total': total,
        'rows': [leave.to_dict() for leave in leaves]  # to_dict()方法需在Leave模型中定义
    })

如果漏掉total字段,BootstrapTable会认为只有一页数据。而to_dict()方法必须显式定义,不能依赖__dict__,因为后者会暴露_sa_instance_state等SQLAlchemy内部字段,导致JSON序列化失败。

BootstrapFileinput上传失败问题
现象:选择文件后进度条不动,控制台报400错误。根本原因是Flask默认禁用multipart/form-data大文件上传。需在app.py开头显式配置:

# app.py 开头
app.config['MAX_CONTENT_LENGTH'] = 16 * 1024 * 1024  # 16MB限制
app.config['UPLOAD_FOLDER'] = 'static/uploads'
app.config['ALLOWED_EXTENSIONS'] = {'pdf', 'jpg', 'jpeg', 'png', 'doc', 'docx'}

# 确保上传目录存在
os.makedirs(app.config['UPLOAD_FOLDER'], exist_ok=True)

前端HTML中,fileinput组件必须设置uploadUrl指向正确的Flask路由,并启用showPreview

<input id="leave-attachment" 
       name="attachment" 
       type="file"
       data-upload-url="{{ url_for('upload_attachment') }}"
       data-show-preview="true"
       data-allowed-file-extensions='["pdf","jpg","jpeg","png"]'>

后端路由upload_attachment需处理文件保存和路径返回:

@app.route('/upload', methods=['POST'])
def upload_attachment():
    if 'attachment' not in request.files:
        return jsonify({'error': 'No file part'}), 400

    file = request.files['attachment']
    if file.filename == '':
        return jsonify({'error': 'No selected file'}), 400

    if file and allowed_file(file.filename):
        filename = secure_filename(file.filename)
        # 添加时间戳避免重名
        timestamp = int(time.time())
        new_filename = f"{timestamp}_{filename}"
        filepath = os.path.join(app.config['UPLOAD_FOLDER'], new_filename)
        file.save(filepath)

        # 返回相对路径供数据库存储
        return jsonify({
            'initialPreview': [f'<img src="/static/uploads/{new_filename}" class="file-preview-image">'],
            'initialPreviewConfig': [{'caption': filename, 'width': '120px', 'url': '/delete_upload', 'key': new_filename}]
        })

    return jsonify({'error': 'File type not allowed'}), 400

这里的关键技巧是:返回的initialPreview必须是HTML字符串,而非纯图片URL,否则BootstrapFileinput无法渲染预览图。而initialPreviewConfig里的url是删除接口,需另行实现。

4. 实操过程与核心环节实现

4.1 从零启动:五分钟完成本地部署

部署流程刻意设计为“无脑操作”,目标是让非技术人员也能完成。以下是严格按顺序执行的步骤,每步附带验证方法:

第一步:环境准备(2分钟)
- 下载Python 3.8+(官网python.org,勾选“Add Python to PATH”)
- 验证:命令行输入python --version,应显示Python 3.8.10或更高
- 创建项目文件夹,解压源码包到该目录

第二步:安装依赖(1分钟)
- 进入项目根目录,执行:
bash pip install -r requirements.txt
- requirements.txt内容精简至核心:
Flask==2.3.3 Flask-SQLAlchemy==3.0.5 Werkzeug==2.3.7 Jinja2==3.1.2 python-dotenv==1.0.0
- 验证:执行pip list | grep Flask,应显示Flask 2.3.3

第三步:初始化数据库(30秒)
- 运行初始化脚本:
bash python data_creator.py
- 验证:检查目录下是否生成data.db文件(大小应>10KB),用DB Browser for SQLite打开,确认users表中有admin用户记录

第四步:启动服务(10秒)
- 执行启动脚本:
bash python launch.py
- launch.py内容极简:
python from app import app if __name__ == '__main__': app.run(debug=True, host='0.0.0.0', port=5000)
- 验证:浏览器访问http://127.0.0.1:5000,出现登录页;用admin/admin123登录(密码见datadb参考手册.pdf第3页)

第五步:角色功能验证(2分钟)
- 登录admin账号,点击“用户管理”,新增一个学生账号(用户名stu001,角色student
- 退出,用stu001登录,进入“我要请假”,填写表单并上传附件
- 用admin账号切换到“辅导员”角色(datadb参考手册.pdf提供角色切换方法),在“待审批”列表看到该申请
- 点击“批准”,状态变为approved,学生端首页显示“已通过”

整个过程无需修改任何代码,所有配置都在config.py中集中管理。launch.py比直接运行app.py更安全,因为它强制加载.env环境变量,避免调试模式意外暴露在公网。

4.2 密码安全实践:generate_password.py的正确用法

generate_password.py不是玩具脚本,而是生产环境密码管理的起点。它的核心价值在于强制使用PBKDF2哈希算法,而非明文存储或弱哈希。

脚本执行方式:

python generate_password.py --password "MySecurePass123" --username "teacher01"

输出示例:

Generated hash for teacher01: pbkdf2:sha256:260000$XyZaBcDeFgHiJkLm$...
Update this in data.db's users table.

关键原理:PBKDF2通过高迭代次数(260000次)和随机盐值(XyZaBcDeFgHiJkLm),让暴力破解成本指数级上升。对比MD5哈希md5("123456")="e10adc3949ba59abbe56e057f20f883e",攻击者用GPU集群1秒可尝试10亿次;而PBKDF2哈希需1秒计算1次,破解一个密码平均耗时数年。

app.py中验证密码的代码必须匹配:

from werkzeug.security import check_password_hash

# 用户登录时
user = User.query.filter_by(username=username).first()
if user and check_password_hash(user.password_hash, password):
    # 登录成功

这里check_password_hash()会自动提取盐值和迭代次数,无需开发者手动解析。generate_password.py生成的哈希字符串中,pbkdf2:sha256:260000$是算法标识,XyZaBcDeFgHiJkLm是盐值,后续是实际哈希值。这种设计保证了即使数据库泄露,攻击者也无法批量破解密码。

4.3 考勤记录页面(attendance.html)的数据联动逻辑

attendance.html表面是静态表格,实则是多张表关联查询的结果。它展示的不是原始请假记录,而是经过状态聚合的考勤视图:

字段数据来源计算逻辑
student_idstudents.id直接关联
real_namestudents.real_name学生真实姓名
class_nameclasses.nameJOIN classes表获取
total_daysSUM(leaves.end_date - leaves.start_date + 1)按学生GROUP BY求和
statusCASE WHEN total_days > 5 THEN ‘缺勤超标’ ELSE ‘正常’ END动态计算

后端路由/api/attendance的SQL查询需优化:

# app.py
@app.route('/api/attendance')
def api_attendance():
    # 使用原始SQL避免ORM复杂JOIN性能损耗
    sql = """
        SELECT 
            s.id as student_id,
            s.real_name,
            c.name as class_name,
            COALESCE(SUM(julianday(l.end_date) - julianday(l.start_date) + 1), 0) as total_days,
            CASE 
                WHEN COALESCE(SUM(julianday(l.end_date) - julianday(l.start_date) + 1), 0) > 5 
                THEN '缺勤超标' 
                ELSE '正常' 
            END as status
        FROM students s
        LEFT JOIN classes c ON s.class_id = c.id
        LEFT JOIN leaves l ON s.id = l.student_id AND l.status = 'approved'
        GROUP BY s.id, s.real_name, c.name
        ORDER BY total_days DESC
        LIMIT 50
    """
    rows = db.session.execute(text(sql)).fetchall()
    return jsonify([dict(row) for row in rows])

这里用julianday()函数计算日期差,比Python端循环计算快10倍;LEFT JOIN确保即使学生从未请假,也会出现在考勤表中(total_days=0);COALESCE处理NULL值,避免前端渲染异常。LIMIT 50是性能保护,防止全校数据一次性加载卡死浏览器。

5. 常见问题与排查技巧实录

5.1 启动报错排查速查表

错误现象可能原因解决方案验证方法
ModuleNotFoundError: No module named 'flask'Python环境未激活或pip安装失败重新执行pip install flask,检查是否在虚拟环境中python -c "import flask; print(flask.__version__)"
OperationalError: no such table: usersdata_creator.py未运行或执行失败删除data.db,重新运行python data_creator.py用DB Browser打开data.db,确认users表存在
jinja2.exceptions.TemplateNotFound: admin.html模板路径错误或文件名大小写不符检查templates/admin.html是否存在,Windows系统注意大小写在文件资源管理器中直接定位该路径
400 Bad Request上传附件失败文件名含中文或特殊字符修改secure_filename()逻辑,或前端限制文件类型用英文名PDF测试上传
500 Internal Server Error登录后白屏session.htmlcurrent_user未定义检查app.py是否正确设置了login_managersession.html中添加{{ current_user.username if current_user else 'None' }}调试

独家技巧:用print()代替日志调试
Flask调试模式下,直接在路由函数中加print(request.form),终端会实时输出表单数据。比配置logging模块更快捷,尤其适合新手定位参数传递问题。

5.2 权限失效的典型场景与修复

场景一:学生能访问教师审批页
现象:学生登录后,手动在地址栏输入/teacher/approvals,页面正常显示。
原因:@require_role('teacher')装饰器未应用到该路由,或装饰器位置错误(写在@app.route上面)。
修复:检查views/teacher.py,确保装饰器紧贴函数定义:

# 正确
@app.route('/teacher/approvals')
@require_role('teacher')
def teacher_approvals():
    return render_template('teacher.html')

# 错误(装饰器位置错)
@require_role('teacher')
@app.route('/teacher/approvals')
def teacher_approvals():
    return render_template('teacher.html')

场景二:管理员看不到所有请假记录
现象:管理员登录后,/admin/all_leaves只显示部分记录。
原因:查询语句中遗漏了status条件,或JOIN时未处理NULL值。
修复:检查admin.py中的查询,必须用LEFT JOIN并允许l.status IS NULL

# 正确:显示所有学生,包括未请假者
db.session.query(Student, Leave).outerjoin(Leave, Student.id == Leave.student_id).all()

# 错误:INNER JOIN导致只显示有请假记录的学生
db.session.query(Student, Leave).join(Leave).all()

场景三:附件上传后路径不显示
现象:上传成功,但leaves.attachment_path字段为空。
原因:前端未正确设置name属性,或后端未从request.files获取。
修复:检查HTML中<input>name必须与后端request.files['xxx']一致:

<!-- 前端 -->
<input name="attachment" type="file">

<!-- 后端 -->
file = request.files['attachment']  # name必须完全匹配

5.3 性能优化与二次开发建议

轻量级优化三板斧
1. 静态资源压缩:将static/css/bootstrap.min.css替换为官方CDN链接,减少本地文件体积
2. 数据库索引添加:在leaves表的student_idstatus字段上建复合索引,加速审批列表查询
sql CREATE INDEX idx_leave_status_student ON leaves(status, student_id);
3. 模板缓存启用:在app.py中添加app.config['TEMPLATES_AUTO_RELOAD'] = True,开发时自动重载,生产环境设为False提升性能

二次开发安全边界
- 新增角色:只需在ROLE_CAPABILITIES字典中添加新键值对,无需改数据库结构
- 扩展字段:如需在请假表加“紧急程度”字段,用SQLite的ALTER TABLE命令:
sql ALTER TABLE leaves ADD COLUMN urgency TEXT DEFAULT 'normal';
- 对接微信通知:在approve_leave()函数末尾添加企业微信API调用,利用requests.post()发送消息,不影响现有流程

最后分享一个小技巧:所有HTML页面底部都有<!-- DEBUG: {{ config.DEBUG }} -->注释,生产环境部署时,把config.py中的DEBUG = False,这段注释自动消失,避免暴露配置信息。这种“开发友好、生产安全”的设计,才是轻量级系统该有的样子。

本文还有配套的精品资源,点击获取 menu-r.4af5f7ec.gif

简介:基于Flask框架开发的轻量级校园请假审批系统,直接运行即可使用。后端用SQLite存储用户资料、请假申请、审批状态和角色权限数据,无需额外安装数据库服务。系统预设管理员、学生、教师、辅导员、教务员五类角色,各自拥有独立首页(admin.html、student.html等)和操作权限,通过role.py统一管理角色逻辑,session.html处理登录态与身份识别。核心功能包括请假提交、多级审批流程、考勤记录查看(attendance.html)、密码生成工具(generate_password.py)及数据库初始化脚本(data_creator.py)。前端采用Bootstrap 5响应式布局,集成jQuery、BootstrapTable实现动态表格展示、BootstrapFileinput支持附件上传、jQuery-Confirm提供交互提示,所有页面继承base.html保持风格统一。静态资源包含7个CSS样式文件、10个JS交互脚本、FontAwesome图标库及favicon.ico。配套提供datadb参考手册.pdf,详细说明data.db中各表字段含义与关联关系,便于二次开发或字段扩展。启动方式简单:安装requirements.txt依赖后运行launch.py或app.py即可访问本地服务。


本文还有配套的精品资源,点击获取
menu-r.4af5f7ec.gif

本文章已经生成可运行项目
内容概要:本文详细介绍了利用二维时域有限差分法(2D FDTD)对光子晶体90度弯曲波导进行数值仿真的Matlab代码实现。该仿真方法旨在精确分析光子晶体波导在弯曲结构下的光传输特性,揭示其导光机制与缺陷模式的调控原理。资源包含完整的Matlab程序代码,支持对空间网格划分、介电常数分布、边界条件(如PML吸收边界)及光源参数等关键仿真要素的灵活设置与优化,便于用户复现结果并开展深入研究。通过仿真可直观获得光场在波导中的传播动态、透射谱特性以及能量损耗情况,为高性能光子器件的设计与优化提供理论依据和技术支持。; 适合人群:具备电磁场理论、光学基础和Matlab编程能力,从事光子学、集成光学或纳米光子器件研究的研究生、科研人员及工程技术开发者。; 使用场景及目标:①学习和掌握FDTD方法在周期性介质(光子晶体)器件仿真中的具体应用流程;②研究90度弯波导的光传输性能,分析弯曲损耗来源并探索低损耗结构优化方案;③作为光子集成电路中关键无源器件的设计与教学参考案例,服务于学术研究与工程实践。; 阅读建议:建议结合光子晶体能带理论与FDTD算法基本原理进行系统学习,运行代码时应逐步调整结构参数与仿真设置,观察光场演化和输出结果的变化,以深化对物理现象的理解,并可在此基础上拓展至其他复杂光子结构(如分束器、谐振腔)的仿真分析。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值