ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

Python Flask财务管理系统毕业设计实战:从数据库设计到报表可视化

Python Flask财务管理系统毕业设计实战:从数据库设计到报表可视化 毕业设计做财务管理系统Python方向的话这个选题可以说是既稳妥又实用。每年都有大量同学选财务类题目但真正能做出完整度、能顺利通过答辩的其实没那么多。我这个项目从需求分析到编码实现再到论文写作全程走完了一遍程序、源码、LW文档论文都齐了今天把完整思路和踩坑经历理一遍给正在做这个题目的同学当个参考。先说清楚这个东西到底是什么。Python财务管理系统简单说就是用一个Web应用或桌面应用把日常的收入、支出、报销、报表统计这些事情管理起来。和Excel记账最大的区别在于它有一套完整的用户体系、数据分类体系、权限模型和可视化报表数据是存进数据库里的可以做复杂查询和统计而不是手动在一张表里拉公式。适合的参考人群很明确计算机相关专业、毕业设计选题涉及Web开发或信息管理系统的同学尤其是准备用Python方向来完成毕设的。我用的技术栈是Python Flask SQLite前端用的是Bootstrap jQuery ECharts架构上采用MTV模式。之所以选择Flask而不是Django核心原因是毕业设计的代码需要能够被清晰讲解Flask的代码量轻、路由逻辑直观、数据库操作可控答辩时老师问到底层实现你能把每一个环节讲清楚这对通过率很有利。1. 项目整体设计与思路拆解1.1 技术选型为什么用Python Flask而不是Django很多同学一开始就会被「框架选择困难症」卡住。Django功能强大、自带Admin后台但它的「魔法」太多了ORM、自动生成的Admin、中间件、信号机制……这些对毕设来说其实是一把双刃剑。你用Django可以很快把网站搭起来但答辩时老师问「你这个用户登录的Session是怎么管理的」「ORM生成的SQL是什么样的」你会发现自己答不上来。Flask则完全不同。它是一个微框架核心只做路由和视图这两件事。数据库操作要么用Flask-SQLAlchemy要么直接写原生SQL表单处理要么用Flask-WTF要么手动接收请求参数。每一步的逻辑都摊在明面上你可以「一行代码一行代码」地给老师讲清楚。我的选型结论框架Flask 2.x轻量、社区文档多、中文资料丰富数据库SQLite 3文件型数据库无需单独安装服务端适合毕设展示和演示ORMFlask-SQLAlchemy保留ORM便利性的同时底层SQL可查、可讲前端Bootstrap 4 jQuery不需要前端工程化模板直接渲染减少调试成本图表可视化ECharts 5财务系统必须有趋势图、占比图答辩加分项这套组合的最大优势是环境好配、代码量适中、逻辑透明。在一台干净的Windows电脑上从装Python到项目跑起来最快20分钟就能完成。1.2 功能模块划分与设计思路财务管理系统属于典型的信息管理系统MIS核心就是增删改查加统计。但直接把需求写成「实现用户的增删改查」肯定拿不到高分必须把业务场景融进去。我最终确定的模块划分用户认证模块登录、注册、退出登录密码Hash存储收入管理模块记录工资、兼职、奖金等收入项支持按类别筛选支出管理模块记录餐饮、购物、交通、住房等支出项支持备注和分类财务管理概览展示总收入、总支出、结余、近期交易记录报表统计模块月度收支趋势、分类占比、自定义日期区间查询分类管理模块内置分类字典也支持用户自定义分类个人中心模块修改密码、查看个人信息这里有一个很关键的设计思路不要做「纯粹的」财务系统而是做「带权限的」财务系统。也就是说系统要区分管理员和普通用户至少要有两个角色。原因很简单毕设题目里如果只有一张表、一个用户自己在录入数据功能就撑不起论文的篇幅答辩时也没有可扩展性可聊。所以我加了一层「家庭成员」概念管理员户主可以查看所有成员的收支记录普通成员只能查看和修改自己的记录。这样一来数据权限过滤就成了系统的核心创新点也就有了技术深度可写。1.3 开发环境与工具准备这一部分看起来基础但很多同学挂在环境上。Python版本我建议直接用3.10或3.11不要用太旧的3.6/3.7Flask新版本对高版本Python支持更好而且新版Python的报错信息更友好。我实际使用的环境清单操作系统Windows 10/1164位Python 3.10.11安装时勾选「Add Python to PATH」PyCharm Community Edition社区版免费够用Flask 2.2.5、Flask-SQLAlchemy 3.0、Werkzeug 2.3数据库可视化工具SQLiteStudio免费轻量或 DBeaver创建虚拟环境这一步强烈建议执行。命令很简单# 在项目根目录打开终端 python -m venv venv # Windows下激活虚拟环境 venv\Scripts\activate # 安装依赖 pip install flask flask-sqlalchemy # 生成依赖清单 pip freeze requirements.txt虚拟环境的作用是隔离不同项目的包版本防止你电脑上之前装过Django或其他版本把环境搞乱。不过要注意的是requirements.txt里生成的版本号很长建议手动精简一下只保留核心依赖。2. 数据库设计与核心数据结构2.1 数据表规划从需求到表结构财务系统的数据表规划我有体会。一开始我直接设计了一张超大的记录表所有字段塞在一起做到后面发现查询越来越乱。实际经验是先想清楚查询需求再反过来定表结构。我的最终表结构是4张表这个规模对毕设来说是恰到好处的——既不是单表那么单薄又没有复杂到难以讲解users 用户表存登录账号、密码Hash、角色、昵称categories 分类表存收支类型收入/支出和分类名称transactions 交易记录表存每一笔收支关联用户和分类budgets 预算表存月度预算可选功能我后期加入的2.2 关键表设计与字段说明用户的表结构如下字段名类型说明idINTEGER 主键自增用户IDusernameVARCHAR(50) 唯一登录名password_hashVARCHAR(128)密码Hash绝不存明文nicknameVARCHAR(50)昵称roleVARCHAR(20)admin / usercreated_atDATETIME创建时间交易记录表结构字段名类型说明idINTEGER 主键自增记录IDuser_idINTEGER 外键关联用户category_idINTEGER 外键关联分类amountFLOAT金额保留2位typeVARCHAR(10)income / expensenoteVARCHAR(200)备注trade_dateDATE交易日期created_atDATETIME创建时间这里有一个非常重要的设计细节金额字段用FLOAT还是DECIMAL财务系统里涉及金钱原则上应该用DECIMAL避免浮点误差但SQLite里对DECIMAL的支持并不完美所以我实际用的是FLOAT但在前端展示和统计时统一用Python的round()做四舍五入同时配合页面上的金额校验规则不允许负数和超过2位小数。如果你用的是MySQL建议直接用DECIMAL(10, 2)更严谨。2.3 数据表创建与初始化使用Flask-SQLAlchemy创建表非常简单以下是模型定义的简化示例from flask_sqlalchemy import SQLAlchemy from datetime import datetime from werkzeug.security import generate_password_hash, check_password_hash db SQLAlchemy() class User(db.Model): __tablename__ users id db.Column(db.Integer, primary_keyTrue) username db.Column(db.String(50), uniqueTrue, nullableFalse) password_hash db.Column(db.String(128), nullableFalse) nickname db.Column(db.String(50)) role db.Column(db.String(20), defaultuser) created_at db.Column(db.DateTime, defaultdatetime.now) def set_password(self, password): self.password_hash generate_password_hash(password) def check_password(self, password): return check_password_hash(self.password_hash, password) class Transaction(db.Model): __tablename__ transactions id db.Column(db.Integer, primary_keyTrue) user_id db.Column(db.Integer, db.ForeignKey(users.id)) category_id db.Column(db.Integer, db.ForeignKey(categories.id)) amount db.Column(db.Float, nullableFalse) type db.Column(db.String(10), nullableFalse) note db.Column(db.String(200)) trade_date db.Column(db.Date, defaultdatetime.now) created_at db.Column(db.DateTime, defaultdatetime.now)在项目首次启动时我会检查数据库文件是否存在如果不存在就自动创建所有表和默认的几类分类数据。这样做的好处是答辩现场换了一台电脑项目也能「干净地」从零跑起来而不用手动导入SQL文件。3. 核心功能实现与关键代码解析3.1 登录与权限控制的实现用户认证是财务系统的第一道关卡。登录的核心安全检查包括用户名是否存在、密码Hash是否匹配、Session会话保持、页面访问权限拦截。我用Flask的session来实现简单的登录状态管理。登录成功后写入用户ID和角色后续每个需要权限的页面都会先检查session中是否有登录标记然后再根据角色判断是否可以访问。from flask import session, redirect, url_for, flash, render_template, request from functools import wraps # 登录装饰器 def login_required(f): wraps(f) def decorated_function(*args, **kwargs): if user_id not in session: return redirect(url_for(auth.login)) return f(*args, **kwargs) return decorated_function # 管理员权限装饰器 def admin_required(f): wraps(f) def decorated_function(*args, **kwargs): if session.get(role) ! admin: flash(需要管理员权限, warning) return redirect(url_for(main.index)) return f(*args, **kwargs) return decorated_function这个装饰器是整个系统的核心安全机制。我在写论文时重点讲解了装饰器的工作机制请求进来先去session里检查登录标记没有就重定向到登录页这个逻辑统一复用防止了每个视图函数里重复写判断代码。这里有个很常见的坑Flask的session默认使用签名Cookie不要在session里存敏感数据比如用户明文密码。只存user_id和role就够了这是安全实践的基本要求。3.2 记账功能收入支出与分类管理记账功能的本质是「往transactions表插入一条记录同时带上用户ID、分类、金额、日期、备注」。但看似简单实际涉及到几个交互细节收入与支出共用一张表还是分开两张表——我共用一张表用type字段区分。这样统计时用SUM结合GROUP BY就能同时算出总收入、总支出不用做表连接。金额合法性校验——前端做一次、后端再做一次。后端校验是必须的不能只依赖前端。代码示例后端处理记账表单main.route(/transaction/add, methods[POST]) login_required def add_transaction(): amount request.form.get(amount) category_id request.form.get(category_id) type request.form.get(type) note request.form.get(note, ).strip() trade_date request.form.get(trade_date) # 后端校验 try: amount float(amount) if amount 0: raise ValueError(金额必须大于0) if round(amount, 2) ! amount: raise ValueError(金额最多保留2位小数) except ValueError as e: flash(str(e), danger) return redirect(request.referrer) txn Transaction( user_idsession[user_id], category_idint(category_id), amountamount, typetype, notenote, trade_datedatetime.strptime(trade_date, %Y-%m-%d) ) db.session.add(txn) db.session.commit() flash(记账成功, success) return redirect(url_for(main.index))我在这个部分有一个体会日期处理一定要统一格式。前端传过来的是字符串后端统一用strptime转成Date类型再入数据库。如果你不转换直接存字符串后续按月统计时会非常痛苦。3.3 数据统计与可视化报表数据可视化是毕业设计最容易拿分的地方因为它「肉眼可见」地体现了系统的实用性。我选用了ECharts通过前后端分离的方式动态加载数据。核心接口返回JSON格式的统计结果main.route(/api/stats) login_required def api_stats(): user_id session[user_id] # 当月收入统计 income db.session.query( func.coalesce(func.sum(Transaction.amount), 0) ).filter( Transaction.user_id user_id, Transaction.type income, func.strftime(%Y-%m, Transaction.trade_date) datetime.now().strftime(%Y-%m) ).scalar() expense db.session.query( func.coalesce(func.sum(Transaction.amount), 0) ).filter( Transaction.user_id user_id, Transaction.type expense, func.strftime(%Y-%m, Transaction.trade_date) datetime.now().strftime(%Y-%m) ).scalar() balance income - expense return jsonify({ income: round(income, 2), expense: round(expense, 2), balance: round(balance, 2) })ECharts在前端通过Ajax获取数据后渲染折线图和饼图。折线图展示近6个月的收支趋势饼图展示支出分类占比。这里有个经验SQLite的日期函数和MySQL不一样。SQLite用strftime(%Y-%m, trade_date)MySQL用DATE_FORMAT(trade_date, %Y-%m)。如果你论文里用的是MySQL代码就要跟着变。很多同学数据库随便换来换去最后代码跑不通基本都是这里出了问题。4. 实操过程与项目部署4.1 从零搭建项目骨架我的项目目录结构如下这个结构对毕设来说很清晰老师打开项目一目了然finance_system/ ├── app.py # 程序入口 ├── config.py # 配置文件 ├── requirements.txt # 依赖清单 ├── models.py # 数据模型 ├── views/ # 蓝图路由 │ ├── __init__.py │ ├── auth.py # 登录注册 │ ├── main.py # 记账与首页 │ └── stats.py # 统计报表 ├── templates/ # HTML模板 │ ├── base.html │ ├── index.html │ ├── login.html │ └── ... ├── static/ # 静态文件 │ ├── css/ │ ├── js/ │ └── echarts/ └── database.db # SQLite数据库运行时自动生成从零搭建的时候我建议按这个顺序来先建项目目录和虚拟环境再安装Flask和Flask-SQLAlchemy然后写models.py定义数据模型再写最简单的「hello world」路由确认基础能跑通最后才逐步叠加登录、记账、统计等功能。千万不要一开始就想着把所有页面都写完连路由还没跑通就写前端页面那是浪费时间。4.2 页面设计Bootstrap模板与交互细节前端我用了Bootstrap的Dashboard模板风格。整体布局是左侧边栏导航、右侧内容区。侧边栏放「首页概览」「记一笔」「收支明细」「统计报表」「分类管理」「个人中心」这些入口顶部栏显示当前登录用户和退出按钮。页面设计的核心是让每一笔账的录入在3秒内完成。所以「记一笔」的界面我做成了表单卡片支出/收入切换按钮、金额输入框、分类下拉框、日期选择器、备注输入框、提交按钮。这个流程极短演示时体验很好。日期选择器我用了原生HTML的input typedate不需要引入第三方插件兼容性好演示也不会出错。收支明细列表使用分页显示每页10条附上「编辑」「删除」操作按钮。编辑和删除都是基于URL传参实现main.route(/transaction/edit/int:txn_id, methods[GET, POST]) login_required def edit_transaction(txn_id): txn Transaction.query.filter_by(idtxn_id, user_idsession[user_id]).first() if not txn: flash(记录不存在或无权操作, danger) return redirect(url_for(main.index)) # 处理POST更新逻辑...这段代码还有一个细节值得注意查询的时候同时带了id和user_id两个条件。这意味着普通用户就算手动改URL里的记录ID也只能操作自己的账目这是一个数据越权的防护点。我在论文里专门讲了这个「水平权限校验」的设计老师反馈说这个点是加分项。4.3 项目打包与演示环境准备毕设演示最怕的是现场环境翻车。我的做法是准备了两套运行方案方案A源码直接运行。这是最常用的。在项目根目录执行python app.py然后浏览器访问 http://127.0.0.1:5000 。方案B使用已有的venv虚拟环境。如果换了一台电脑没有安装依赖那就先激活虚拟环境再运行没有虚拟环境的话手动装依赖pip install -r requirements.txt python app.py如果运行时报错找不到flask多半是没激活虚拟环境或者pip安装到了系统Python而不是当前环境的Python。这个排查思路非常重要我后面会在常见问题里展开。数据库初始化方面我没有用额外的SQL脚本。项目启动时如果检测到database.db不存在就自动创建表和默认分类数据。这样做的好处是换电脑演示时不需要拷贝数据库文件项目一键可用。5. 常见问题与排查技巧实录5.1 数据库相关报错与处理跑毕设项目数据库是翻车重灾区。我整理了三类高频问题第一类数据库文件锁死。SQLite在Windows下偶尔会出现「database is locked」报错。这通常是因为有另一个进程比如SQLiteStudio打开了同一个.db文件同时Flask也在写入。解决办法是关闭所有外部数据库工具或者在代码中配置连接超时app.config[SQLALCHEMY_ENGINE_OPTIONS] { connect_args: {timeout: 15} }第二类users表已经存在。这是重复运行create_all导致的。如果你修改了models.py里的字段但旧的数据库文件还在新代码不会自动同步表结构。此时最干脆的处理方式就是删除database.db让它重新生成或者用迁移工具Flask-Migrate但毕设没必要。第三类外键约束失效。SQLite默认不启用外键约束所以代码里写了db.ForeignKey但实际可能不生效。如果你需要级联删除效果必须在连接后执行PRAGMA foreign_keys ON。我在代码里用了before_request钩子来统一处理from sqlalchemy import event event.listens_for(Engine, connect) def set_sqlite_pragma(dbapi_connection, connection_record): cursor dbapi_connection.cursor() cursor.execute(PRAGMA foreign_keysON) cursor.close()这里想提醒一点外键约束在毕设里其实不是很关键的功能但如果你在论文里写了「数据库设计了外键关联」那代码里就真的要保证外键生效否则就是理论与实现脱节答辩时会很尴尬。5.2 中文编码乱码问题Windows环境下运行Flask中文乱码一般有两个原因。第一个原因是Python文件没有声明UTF-8编码。Python 3默认就是UTF-8所以现在通常不会因为文件编码而乱码。真正容易出问题的是控制台输出。如果你在终端里print中文Windows的GBK编码有时候会报UnicodeEncodeError。解决方案是在程序入口处加一行import sys import io sys.stdout io.TextIOWrapper(sys.stdout.buffer, encodingutf-8)第二个原因是浏览器显示乱码。Flask的模板文件只要在HTML里有meta charsetutf-8基本不会乱。如果发现了乱码优先检查数据库里的数据本身是否是乱码——用SQLiteStudio打开看一下就知道。如果库里就是乱码那是写入的时候出了问题跟浏览器无关。5.3 答辩前必须检查的细节清单这部分内容是基于我的血泪教训整理的建议你在答辩前逐项打钩[ ] 注册新用户后能正常登录并记账[ ] 用管理员账号能看到所有用户的数据[ ] 修改密码后旧密码不能再登录[ ] 删除一笔记录后有确认提示避免误删[ ] 金额输入负数、0、小数超过2位后系统会报错[ ] 统计报表的「近6个月趋势图」在不同月份数据显示正确[ ] 所有页面在浏览器缩放状态下布局不塌[ ] 数据库文件不在桌面演示时被意外改动我吃过一个亏答辩前发现统计图的日期显示错位。原因是前端格式化日期时用了JS的Date对象而JS的月份是从0开始计的0代表1月。如果你在代码里也遇到了类似问题记得检查一下月份是否要加1。6. LW文档写作与毕业答辩经验6.1 LW文档的结构拆解与写作技巧LW文档是毕业设计的论文部分很多同学代码做完了卡在写论文上。我总结了一套逻辑按这个结构写行文会很顺畅绪论背景、意义、国内外研究现状、主要工作相关技术介绍Python、Flask、SQLite、ECharts系统分析可行性分析、需求分析、用例图、数据流图系统设计总体架构、功能模块设计、数据库设计系统实现每个模块的界面截图和核心代码说明系统测试测试用例设计、测试结果总结与展望这里有一个关键写作思路每个功能模块的「实现」小节不要只贴代码要写「为什么这么做」。比如你写了用装饰器做登录校验就要解释装饰器相比在每个视图里复制粘贴判断代码的优势你写了用ECharts而不是Highcharts就要说ECharts的文档和中文社区更友好对国内用户更友好。论文里插入的每个截图都要配上文字说明不能光秃秃一个图。格式方面图和表格要有编号表格用三线表参考文献要按学校要求格式排列。6.2 答辩演示的流程与常见问题应答答辩演示时我实际走了一遍这个流程效果很稳先花2分钟介绍项目背景和技术栈然后演示登录最好注册一个新账号现场演示录入一笔支出、一笔收入展示收支概览和图表统计展示管理员视角的数据最后总结项目亮点和不足答辩老师最常问的问题我提前整理了答案问为什么选SQLite而不是MySQL 答SQLite是嵌入式关系型数据库零配置、单文件存储适合轻量级单机应用和课程设计场景如果未来扩展到多用户高并发场景可以通过SQLAlchemy的适配层平滑切换到MySQL或PostgreSQL。问密码安全是怎么做的 答密码通过Werkzeug库的generate_password_hash进行PBKDF2加盐哈希存储数据库中不存明文密码登录时通过check_password_hash校验即使数据库泄露口令原文也不会直接暴露。问系统有什么不足 答目前只实现了基础的记账和统计未来可以增加预算管理、定期账单提醒、数据导出Excel、多币种支持等功能。用「未来改进」来展示你的思考深度。这样的应答策略帮了大忙。老师问的问题并不深关键是你得对自己的代码每一层都心里有数回答时不要吞吞吐吐尽量结合代码中的具体实现来讲。最后再分享一个我在实际开发中的经验做这种信息管理系统真正拉长时间的不是写代码而是反复调试的边界情况。比如金额校验、日期格式、权限拦截这些都是细节但恰恰是这些细节决定了一个毕设看起来像「应付交差」还是「认真完成」。如果你时间紧张优先把登录、记账、统计三个核心链路打磨顺然后保证代码能跑、论文能讲清楚这个题目的完成度就不会低。
返回列表