管理系统模板
在 Web 开发学习过程中,亲手搭建一个功能完整的管理系统是巩固技术栈的绝佳方式。本文将带大家从零开始,用 HTML+JS+CSS 搭建前端界面,结合 Flask 后端框架与 SQLite 数据库,开发一个轻量实用的学生管理系统,适合全栈初学者参考实践。
技术栈解析
- 前端三剑客:HTML 构建页面结构,CSS 实现响应式布局与美化,JavaScript 处理表单验证、动态交互(如数据加载、删除确认)。
- 后端框架:Flask(Python),轻量级 Web 框架,路由配置简单,适合小型项目快速开发,无需复杂配置即可连接前端与数据库。
- 数据库:SQLite,无需独立服务的文件型数据库,自带 Python 标准库,与 Flask 结合紧密,适合开发阶段和小规模应用。
这套组合的优势在于轻量易部署,无需额外安装数据库服务,单文件即可运行,对新手友好度拉满。
系统功能设计与架构
核心功能模块
- 用户登录:验证管理员身份(简化版可跳过注册,直接使用预设账号)。
- 学生信息管理:
- 列表展示:学号、姓名、性别、年龄、班级等字段。
- 增删改查:添加新学生、编辑已有信息、删除退学学生、按学号 / 姓名搜索。
- 数据持久化:所有操作实时同步到 SQLite 数据库,刷新页面不丢失数据。
架构流程
前端页面(HTML+CSS) ←→ JavaScript(交互/请求) ←→ Flask后端(路由/逻辑) ←→ SQLite数据库(存储)
- 前端通过 Ajax 发送请求(如添加学生时提交表单数据)。
- Flask 接收请求后,调用数据库操作函数(增删改查)。
- 数据库返回结果,Flask 处理后将数据以 JSON 或渲染模板的形式返回给前端。
- 前端更新页面显示(如刷新列表、提示操作结果)。
核心代码实现
from flask import Flask, render_template, request, redirect, url_for, session, send_file, flashimport sqlite3, os, csvfrom werkzeug.security import generate_password_hash, check_password_hashapp = Flask(__name__)app.secret_key = \'super-secret-key\'DB_PATH = \'database/students.db\'# --- DB 初始化 ---def init_db(): if not os.path.exists(\'database\'): os.makedirs(\'database\') conn = sqlite3.connect(DB_PATH) cursor = conn.cursor() cursor.execute(\'\'\' CREATE TABLE IF NOT EXISTS users ( id INTEGER PRIMARY KEY AUTOINCREMENT, username TEXT UNIQUE, password TEXT, role TEXT DEFAULT \'user\' ) \'\'\') cursor.execute(\'\'\' CREATE TABLE IF NOT EXISTS students ( id INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT, age INTEGER, grade TEXT, score REAL ) \'\'\') conn.commit() conn.close() # 添加默认管理员账户(如果不存在) conn = sqlite3.connect(DB_PATH) cursor = conn.cursor() cursor.execute(\"SELECT COUNT(*) FROM users WHERE username = \'admin\'\") if cursor.fetchone()[0] == 0: hashed_password = generate_password_hash(\'admin123\') cursor.execute(\"INSERT INTO users (username, password, role) VALUES (?, ?, ?)\", (\'admin\', hashed_password, \'admin\')) conn.commit() conn.close()# --- DB 操作函数 ---def get_db(): conn = sqlite3.connect(DB_PATH) conn.row_factory = sqlite3.Row return conn# --- 首页重定向 ---@app.route(\'/\')def index(): return redirect(url_for(\'login\'))# --- 注册 ---@app.route(\'/register\', methods=[\'GET\', \'POST\'])def register(): if request.method == \'POST\': username = request.form[\'username\'] password = generate_password_hash(request.form[\'password\']) try: conn = get_db() conn.execute(\'INSERT INTO users (username, password) VALUES (?, ?)\', (username, password)) conn.commit() flash(\'注册成功,请登录\', \'success\') return redirect(url_for(\'login\')) except: flash(\'用户名已存在\', \'danger\') return render_template(\'register.html\')# --- 登录 ---@app.route(\'/login\', methods=[\'GET\', \'POST\'])def login(): if request.method == \'POST\': username = request.form[\'username\'] password_input = request.form[\'password\'] conn = get_db() user = conn.execute(\'SELECT * FROM users WHERE username = ?\', (username,)).fetchone() if user and check_password_hash(user[\'password\'], password_input): session[\'user\'] = username session[\'role\'] = user[\'role\'] # 保存用户角色 return redirect(url_for(\'dashboard\')) flash(\'用户名或密码错误\', \'danger\') return render_template(\'login.html\')# --- 登出 ---@app.route(\'/logout\')def logout(): session.pop(\'user\', None) return redirect(url_for(\'login\'))# --- 学生信息主页面 ---@app.route(\'/dashboard\')def dashboard(): if \'user\' not in session: return redirect(url_for(\'login\')) conn = get_db() students = conn.execute(\'SELECT * FROM students\').fetchall() return render_template(\'dashboard.html\', students=students)# --- 数据库管理页面 ---@app.route(\'/db_admin\')def db_admin(): # 检查用户权限 if \'user\' not in session or session.get(\'role\') != \'admin\': flash(\'权限不足\', \'danger\') return redirect(url_for(\'dashboard\')) databases = list_databases() return render_template(\'db_admin.html\', databases=databases)# --- 创建新数据库 ---@app.route(\'/create_db\', methods=[\'POST\'])def create_db(): # 检查用户权限 if \'user\' not in session or session.get(\'role\') != \'admin\': flash(\'权限不足\', \'danger\') return redirect(url_for(\'dashboard\')) db_name = request.form.get(\'db_name\') if db_name: try: db_path = create_new_database(db_name) flash(f\'数据库 {db_name} 创建成功\', \'success\') except Exception as e: flash(f\'创建数据库失败: {str(e)}\', \'danger\') else: flash(\'数据库名称不能为空\', \'danger\') return redirect(url_for(\'db_admin\'))# --- 删除数据库 ---@app.route(\'/delete_db/\')def delete_db(db_name): # 检查用户权限 if \'user\' not in session or session.get(\'role\') != \'admin\': flash(\'权限不足\', \'danger\') return redirect(url_for(\'dashboard\')) if db_name != \'students\': # 防止删除主数据库 if delete_database(db_name): flash(f\'数据库 {db_name} 删除成功\', \'success\') else: flash(f\'数据库 {db_name} 删除失败\', \'danger\') else: flash(\'不能删除主数据库\', \'danger\') return redirect(url_for(\'db_admin\'))# --- 查看数据库详情 ---@app.route(\'/db_info/\')def db_info(db_name): # 检查用户权限 if \'user\' not in session or session.get(\'role\') != \'admin\': flash(\'权限不足\', \'danger\') return redirect(url_for(\'dashboard\')) info = get_database_info(db_name) if info: return render_template(\'db_info.html\', info=info) else: flash(\'数据库不存在\', \'danger\') return redirect(url_for(\'db_admin\'))# --- 添加学生 ---# 关键修改:添加 \'GET\' 方法支持@app.route(\'/add_student\', methods=[\'GET\', \'POST\'])def add_student(): # 如果是GET请求,显示新增学生表单 if request.method == \'GET\': return render_template(\'addstudent.html\') # 如果是POST请求,处理表单提交(原逻辑) name = request.form[\'name\'] age = request.form[\'age\'] grade = request.form[\'grade\'] score = request.form[\'score\'] # 数据校验(推荐添加) try: age = int(age) score = float(score) if age 150 or score 100: flash(\'年龄或成绩格式错误\', \'error\') return render_template(\'addstudent.html\') except ValueError: flash(\'年龄或成绩必须为数字\', \'error\') return render_template(\'addstudent.html\') # 数据库操作 try: conn = get_db() conn.execute(\'INSERT INTO students (name, age, grade, score) VALUES (?, ?, ?, ?)\', (name, age, grade, score)) conn.commit() flash(\'学生添加成功\', \'success\') return redirect(url_for(\'dashboard\')) except Exception as e: flash(f\'添加失败:{str(e)}\', \'error\') return render_template(\'addstudent.html\')# --- 删除学生 ---@app.route(\'/delete_student/\')def delete_student(id): conn = get_db() conn.execute(\'DELETE FROM students WHERE id = ?\', (id,)) conn.commit() return redirect(url_for(\'sort\'))# --- 修改学生 ---@app.route(\'/edit_student/\', methods=[\'GET\', \'POST\'])def edit_student(id): conn = get_db() if request.method == \'POST\': name = request.form[\'name\'] age = request.form[\'age\'] grade = request.form[\'grade\'] score = request.form[\'score\'] conn.execute(\'UPDATE students SET name=?, age=?, grade=?, score=? WHERE id=?\', (name, age, grade, score, id)) conn.commit() return redirect(url_for(\'sort\')) student = conn.execute(\'SELECT * FROM students WHERE id=?\', (id,)).fetchone() return render_template(\'edit_student.html\', student=student)# --- 查询学生 ---@app.route(\'/search\', methods=[\'GET\'])def search(): keyword = request.args.get(\'q\', \'\') conn = get_db() students = conn.execute(\'SELECT * FROM students WHERE name LIKE ?\', (f\'%{keyword}%\',)).fetchall() return render_template(\'dashboard.html\', students=students)# --- 成绩排序 ---@app.route(\'/sort\')def sort(): # 获取排序参数,默认升序 order = request.args.get(\'order\', \'asc\').lower() # 限定合法值,避免非法参数 if order not in [\'asc\', \'desc\']: order = \'asc\' # 拼接排序 SQL(这里因排序字段和方式简单,参数化意义不大,仍用拼接但做好校验) query = f\'SELECT * FROM students ORDER BY score {\"ASC\" if order == \"asc\" else \"DESC\"}\' try: conn = get_db() students = conn.execute(query).fetchall() except Exception as e: # 捕获数据库异常,返回友好提示(可结合 flash 消息 ) flash(f\'查询成绩排序数据失败:{str(e)}\', \'error\') return redirect(url_for(\'dashboard\')) # 传递当前排序方式到模板,用于展示和切换 return render_template(\'score_sort.html\', students=students, current_order=order)# --- 导出 CSV ---@app.route(\'/export_csv\')def export_csv(): conn = get_db() students = conn.execute(\'SELECT * FROM students\').fetchall() export_path = \'export/report.csv\' if not os.path.exists(\'export\'): os.makedirs(\'export\') with open(export_path, \'w\', newline=\'\', encoding=\'utf-8\') as f: writer = csv.writer(f) writer.writerow([\'ID\', \'姓名\', \'年龄\', \'年级\', \'成绩\']) for s in students: writer.writerow([s[\'id\'], s[\'name\'], s[\'age\'], s[\'grade\'], s[\'score\']]) return send_file(export_path, as_attachment=True)# --- 成绩可视化图表 ---@app.route(\'/chart\')def chart(): conn = get_db() data = conn.execute(\'SELECT name, score FROM students\').fetchall() # 格式化成绩为保留一位小数 formatted_data = [(name, round(score, 1)) for name, score in data] return render_template(\'chart.html\', data=formatted_data)if __name__ == \'__main__\': init_db() app.run(debug=True)