欢迎光临
我们一直在努力

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)
    未经允许不得转载:171主机测评 » Python之Flask开发框架-项目实战生活管理助手
    分享到: 更多 (0)

    评论 抢沙发

    • 昵称 (必填)
    • 邮箱 (必填)
    • 网址