Python之Flask开发框架-项目实战生活管理助手
·
本教程将带你从零搭建一个带用户系统的个人生活管理应用,包含待办提醒、目标倒计时、记账三大核心功能。后端使用 Flask + SQLAlchemy + Flask-Login,前端使用原生 HTML/CSS/JS 与 jQuery。通过本项目,你将掌握完整的 Web 开发流程,并理解用户认证、数据隔离、REST API 设计等关键概念。
一、项目预览
- 用户系统:注册、登录、登出,每个用户的数据完全隔离。
- 待办模块:创建任务、设置提醒时间、标记完成/未完成,到期自动弹窗提醒。
- 目标模块:设定目标与截止日期,实时显示倒计时(天/时/分)。
- 记账模块:记录收入/支出,按分类统计,自动计算余额。
技术栈:
- 后端:Flask (Python Web 框架)
- 数据库:SQLite + SQLAlchemy (ORM)
- 用户认证:Flask-Login
- 前端:jQuery (AJAX 请求)、原生 JavaScript、响应式 CSS
二、环境准备与项目初始化
2.1 安装 Python
确保你的电脑已安装 Python 3.8 或更高版本。在终端输入:
python --version
2.2 创建项目文件夹
mkdir life_manager
cd life_manager
2.3 创建虚拟环境(推荐)
python -m venv venv
# 激活虚拟环境
# Windows:
venv\Scripts\activate
# Mac/Linux:
source venv/bin/activate
2.4 安装依赖
创建 requirements.txt 文件,内容如下:
Flask==2.3.3
Flask-SQLAlchemy==3.1.1
Flask-Login==0.6.2
然后执行:
pip install -r requirements.txt
2.5 项目结构
life_manager/
├── app.py # 主程序
├── templates/ # 模板文件夹
│ ├── login.html
│ ├── register.html
│ └── index.html
└── life_manager.db # 运行后自动生成(SQLite数据库)
将本教程后面提供的三个 HTML 文件放入 templates 文件夹,将 app.py 放在根目录。
三、后端代码详解(app.py)
3.1 导入模块与配置
from flask import Flask, request, jsonify, render_template, redirect, url_for, flash
from flask_sqlalchemy import SQLAlchemy
from flask_login import LoginManager, UserMixin, login_user, logout_user, login_required, current_user
from datetime import datetime
from werkzeug.security import generate_password_hash, check_password_hash
Flask:核心框架。render_template:渲染 HTML 模板。redirect/url_for:页面跳转。flash:闪现消息(用于登录错误提示)。SQLAlchemy:ORM,操作数据库。LoginManager、UserMixin等:处理用户会话。werkzeug.security:密码哈希与校验。
配置部分:
app = Flask(__name__)
app.config['SECRET_KEY'] = 'your-secret-key-here' # 请改为随机字符串
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///life_manager.db'
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False
db = SQLAlchemy(app)
login_manager = LoginManager()
login_manager.init_app(app)
login_manager.login_view = 'login' # 未登录时跳转到 login 路由
3.2 数据库模型
用户模型(User)
class User(UserMixin, db.Model):
id = db.Column(db.Integer, primary_key=True)
username = db.Column(db.String(80), unique=True, nullable=False)
password_hash = db.Column(db.String(200), nullable=False)
tasks = db.relationship('Task', backref='user', lazy=True, cascade='all, delete-orphan')
goals = db.relationship('Goal', backref='user', lazy=True, cascade='all, delete-orphan')
transactions = db.relationship('Transaction', backref='user', lazy=True, cascade='all, delete-orphan')
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)
UserMixin提供了is_authenticated等属性,简化 Flask-Login 集成。relationship定义了与子表的一对多关系,cascade='all, delete-orphan'表示删除用户时自动删除其所有关联数据。set_password/check_password负责密码加密与验证。
待办模型(Task)
class Task(db.Model):
id = db.Column(db.Integer, primary_key=True)
title = db.Column(db.String(200), nullable=False)
remind_time = db.Column(db.DateTime, nullable=True)
completed = db.Column(db.Boolean, default=False)
user_id = db.Column(db.Integer, db.ForeignKey('user.id'), nullable=False)
目标模型(Goal)
class Goal(db.Model):
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(200), nullable=False)
target_date = db.Column(db.DateTime, nullable=False)
user_id = db.Column(db.Integer, db.ForeignKey('user.id'), nullable=False)
记账模型(Transaction)
class Transaction(db.Model):
id = db.Column(db.Integer, primary_key=True)
type = db.Column(db.String(10), nullable=False) # 'income' 或 'expense'
category = db.Column(db.String(50), nullable=False)
amount = db.Column(db.Float, nullable=False)
date = db.Column(db.DateTime, default=datetime.now)
note = db.Column(db.String(200))
user_id = db.Column(db.Integer, db.ForeignKey('user.id'), nullable=False)
每个模型都通过 user_id 外键关联到用户,实现数据隔离。
3.3 创建数据库表与测试用户
with app.app_context():
db.drop_all() # 仅在开发时使用,会清空已有数据
db.create_all()
# 创建一个演示用户 demo / demo123
if not User.query.filter_by(username='demo').first():
demo = User(username='demo')
demo.set_password('demo123')
db.session.add(demo)
db.session.commit()
db.drop_all()会删除所有表,适合开发阶段快速重置结构。生产环境切勿使用,应用 Alembic 等迁移工具。
3.4 用户认证路由
登录路由
@app.route('/login', methods=['GET', 'POST'])
def login():
if current_user.is_authenticated:
return redirect(url_for('index'))
if request.method == 'POST':
username = request.form.get('username')
password = request.form.get('password')
user = User.query.filter_by(username=username).first()
if user and user.check_password(password):
login_user(user)
next_page = request.args.get('next')
return redirect(next_page or url_for('index'))
flash('用户名或密码错误', 'error')
return render_template('login.html')
current_user.is_authenticated:检查是否已登录。login_user(user):Flask-Login 提供的函数,建立会话。next参数:实现登录后跳转到原来想访问的页面。
注册路由
@app.route('/register', methods=['GET', 'POST'])
def register():
# 类似,校验两次密码是否一致,用户名是否已存在
# 创建新用户后调用 login_user 或跳转到登录页
登出
@app.route('/logout')
@login_required
def logout():
logout_user()
return redirect(url_for('login'))
@login_required装饰器确保只有登录用户才能访问此路由。
3.5 API 设计(用户数据隔离)
以任务模块为例,其他模块结构完全相同。
获取当前用户的所有任务
@app.route('/api/tasks', methods=['GET'])
@login_required
def get_tasks():
tasks = Task.query.filter_by(user_id=current_user.id).all()
return jsonify([...])
关键点:filter_by(user_id=current_user.id) 确保只返回属于当前登录用户的数据。
创建任务
@app.route('/api/tasks', methods=['POST'])
@login_required
def create_task():
data = request.get_json()
task = Task(title=data['title'], ..., user_id=current_user.id)
db.session.add(task)
db.session.commit()
return jsonify({'id': task.id}), 201
更新/删除任务
@app.route('/api/tasks/<int:task_id>', methods=['PUT'])
@login_required
def update_task(task_id):
task = Task.query.filter_by(id=task_id, user_id=current_user.id).first_or_404()
# 修改并提交
first_or_404()会自动返回 404 如果不存在或不属于当前用户,避免了越权访问。
记账与目标的 API 模式完全一致,只需替换模型类名即可。
四、前端页面详解
4.1 登录与注册页面(login.html / register.html)
- 使用简单的居中卡片样式,表单提交到同路由的 POST 方法。
- 通过
flash消息显示错误或成功提示。 - 注册页面额外增加“确认密码”字段,前端验证与后端双重保证。
4.2 主界面(index.html)
用户信息与登出按钮
<div class="user-area">
<span>👤 {{ username }}</span>
<a href="{{ url_for('logout') }}" class="logout-btn">退出</a>
</div>
{{ username }}是 Jinja2 模板语法,从后端render_template('index.html', username=current_user.username)传递。
选项卡切换
$('.tab').on('click', function() {
$('.tab').removeClass('active');
$(this).addClass('active');
const panelId = $(this).data('panel') + 'Panel';
$('.panel').removeClass('active-panel');
$('#' + panelId).addClass('active-panel');
});
- 使用
data-panel属性存储面板标识,通过 jQuery 动态显示对应面板。
AJAX 数据交互
所有增删改查都通过 AJAX 调用后端 API,并实时刷新界面。
获取待办列表:
function loadTodos() {
$.get('/api/tasks', function(tasks) {
// 渲染列表,并调用 checkReminders 检测提醒
}).fail(handleUnauth);
}
handleUnauth函数:若返回 401 状态码,则跳转到登录页。
添加待办:
$.ajax({
url: '/api/tasks',
method: 'POST',
contentType: 'application/json',
data: JSON.stringify({ title: title, remind_time: remindTime || null }),
success: () => loadTodos()
});
提醒机制
- 每次加载待办列表时,遍历所有未完成且设置了提醒时间的任务。
- 若当前时间 >= 提醒时间,且该任务 ID 未被记录到
remindedTaskIds数组,则显示横幅提醒。 - 使用
setInterval每分钟重新加载待办列表,实现准实时提醒。
倒计时计算
const diffMs = targetDate - now;
const days = Math.floor(diffMs / (1000*60*60*24));
const hours = Math.floor((diffMs % (86400000)) / (3600000));
const minutes = Math.floor((diffMs % 3600000) / 60000);
余额计算
let total = 0;
trans.forEach(t => {
total += (t.type === 'income' ? t.amount : -t.amount);
});
XSS 防护
在渲染用户输入的内容时(如任务标题、备注),使用 escapeHtml 函数转义 &<> 字符,防止跨站脚本攻击。
五、项目运行与测试
- 确保虚拟环境已激活,依赖已安装。
- 在项目根目录下执行:
python app.py - 浏览器访问
http://127.0.0.1:5000 - 可使用演示账号
demo/demo123登录,或注册新账号。 - 尝试添加待办、目标、记账记录,体验数据隔离效果(不同账号数据互不可见)。
六、知识点深度解析
6.1 Flask 核心概念
- 路由(Route):通过
@app.route将 URL 绑定到 Python 函数,支持 GET、POST 等方法。 - 请求对象(request):获取表单数据(
request.form)、JSON 数据(request.get_json())、查询参数(request.args)。 - 响应(Response):
render_template返回 HTML,jsonify返回 JSON,redirect重定向。 - 会话(Session):Flask 默认基于客户端 cookie 的会话,Flask-Login 在此基础上管理用户登录状态。
6.2 SQLAlchemy ORM
- 模型定义:类继承自
db.Model,属性映射为数据库列。 - 关系(relationship):定义表之间的关联,
backref允许反向访问,lazy=True表示在访问时自动查询。 - 增删改查:
- 查询:
Model.query.filter_by(条件).all()、.first()、.get_or_404() - 添加:
db.session.add(实例)→db.session.commit() - 删除:
db.session.delete(实例)→commit()
- 查询:
- 外键约束:
db.ForeignKey('user.id')确保数据完整性。
6.3 Flask-Login 认证流程
- 用户加载回调:
@login_manager.user_loader根据用户 ID 返回 User 对象。 - 登录:
login_user(user)将用户 ID 存入 session。 - 登出:
logout_user()清除 session。 - 保护路由:
@login_required装饰器,未登录时自动跳转到login_view指定的路由。 - 模板中获取用户:
current_user全局变量,可直接在视图和模板中使用。
6.4 密码安全
- 哈希加盐:
generate_password_hash使用随机盐和 PBKDF2 算法,不可逆。 - 验证:
check_password_hash对比明文与哈希值。 - 绝不存储明文密码。
6.5 REST API 设计原则
- 使用 HTTP 方法表示操作:GET(获取)、POST(创建)、PUT(更新)、DELETE(删除)。
- URL 使用资源复数形式:
/api/tasks,对特定资源:/api/tasks/<id>。 - 返回 JSON 格式数据,并携带合适的 HTTP 状态码(200、201、404 等)。
- 无状态:每个请求独立,服务器不保存客户端上下文(除 session 外)。
6.6 前后端交互(AJAX)
- jQuery 的
$.get、$.post、$.ajax简化异步请求。 - 跨站请求伪造(CSRF)防护:Flask 默认不启用,生产环境需添加 CSRF token(如 Flask-WTF)。
- 错误处理:统一处理 401 状态码,跳转登录。
6.7 前端数据渲染与用户体验
- 动态生成 HTML 并插入 DOM,避免页面刷新。
- 使用
setInterval定时轮询,模拟实时提醒(也可使用 WebSocket 实现更高效推送)。 - 响应式布局:使用 flex、grid、百分比宽度适配移动端。
七、常见问题与排查
| 问题 | 可能原因 | 解决方法 |
|---|---|---|
运行后报 No module named flask |
未激活虚拟环境或未安装依赖 | 激活 venv,执行 pip install -r requirements.txt |
| 登录后仍提示未登录 | SECRET_KEY 未设置或 session 失效 |
检查 app.config['SECRET_KEY'],确保固定且随机 |
| API 返回 401 | 未登录或 session 过期 | 清除浏览器 cookie 后重新登录 |
| 提醒横幅不出现 | 待办提醒时间格式不对 | 检查 remind_time 是否为 ISO 格式字符串,前端 new Date() 能正确解析 |
| 倒计时显示 NaN | 目标截止日期字段为 NULL | 确保添加目标时填写了有效日期 |
| 数据混乱(看到他人数据) | 查询时未过滤 user_id |
检查每个 API 是否添加了 filter_by(user_id=current_user.id) |
八、总结
通过本项目,你亲手搭建了一个完整的多用户 Web 应用,涵盖了认证、数据库操作、前后端分离交互等核心技能。你可以将本项目作为个人效率工具使用,也可以继续扩展,加入更多功能(如共享清单、月度报表等)。
希望这篇教程能帮助你巩固 Flask 全栈开发的知识,并激发更多创造灵感。祝你编码愉快!
更多推荐


所有评论(0)