Flask+SQLite简易后端数据存储实现原理

Flask+SQLite简易后端数据存储实现原理

核心观点

Flask框架配合SQLite数据库,通过请求级连接管理、参数化查询和数据校验三层机制,实现了轻量、安全、易维护的后端数据存储方案,非常适合小型项目原型开发、教学演示和单机应用场景。

架构流程说明:
用户请求
Flask路由层
业务逻辑层

数据校验
数据访问层

SQL操作
SQLite数据库

goods.db

背景与现状

在Web应用开发领域,数据持久化是最基础也是最核心的功能之一。传统企业级方案往往涉及复杂的ORM配置、连接池管理、事务处理等概念,对初学者形成较高的学习门槛。

当前主流方案存在以下问题:

  • MySQL/PostgreSQL需要独立安装和运维数据库服务
  • ORM框架(如SQLAlchemy)学习曲线陡峭
  • 过度工程化导致小型项目开发效率低下

Flask+SQLite方案的优势在于:

  • 零配置启动,Python内置sqlite3模块
  • 单文件存储,便于备份和迁移
  • 轻量级架构,核心代码不超过200行
  • 适合快速原型开发和教学演示

✅ Flask+SQLite方案
Python内置sqlite3

零安装
单文件存储

goods.db
核心代码<200行

轻量架构
快速原型开发

效率高
❌ 传统企业级方案
MySQL/PostgreSQL

独立服务进程
ORM框架配置

学习曲线陡峭
连接池管理

复杂度高
过度工程化

效率低下

关键要素

要素 1:SQLite数据库特性

SQLite是一个嵌入式关系数据库,将整个数据库存储在单个磁盘文件中。与MySQL/PostgreSQL需要独立服务进程不同,SQLite通过文件直接读写,无需网络连接。

核心特性:

  • 零安装:Python标准库内置sqlite3模块
  • 单文件:所有数据存储在goods.db一个文件中
  • ACID事务:支持完整的事务特性
  • 跨平台:同一数据库文件可在不同操作系统使用
  • 读并发:多个读取操作可并发执行
  • 写串行:写入操作自动加锁排队

数据库初始化代码:

python 复制代码
import sqlite3
import os

DB_PATH = os.path.join(os.path.dirname(os.path.abspath(__file__)), "goods.db")

def init_db():
    """初始化数据库表结构"""
    db = sqlite3.connect(DB_PATH)
  
    # 创建商品表
    db.execute("""
        CREATE TABLE IF NOT EXISTS goods (
            id INTEGER PRIMARY KEY AUTOINCREMENT,
            name TEXT NOT NULL,
            price REAL NOT NULL,
            stock INTEGER NOT NULL DEFAULT 0,
            category TEXT NOT NULL DEFAULT '',
            description TEXT NOT NULL DEFAULT '',
            created_at TEXT NOT NULL
        )
    """)
  
    # 动态检测并添加新字段(支持数据库迁移)
    cur = db.execute("PRAGMA table_info(goods)")
    columns = {row[1] for row in cur.fetchall()}
  
    if "stock" not in columns:
        db.execute("ALTER TABLE goods ADD COLUMN stock INTEGER NOT NULL DEFAULT 0")
  
    if "created_at" not in columns:
        db.execute("ALTER TABLE goods ADD COLUMN created_at TEXT NOT NULL DEFAULT ''")
  
    db.commit()
    db.close()

# 应用启动时自动初始化
init_db()



应用启动
init_db()
连接goods.db文件
CREATE TABLE IF NOT EXISTS

幂等性保证
PRAGMA table_info

检测已有字段
需要迁移?
ALTER TABLE

添加新字段
提交事务
关闭连接

准备就绪

要素 2:Flask的g对象机制

Flask的g对象是请求上下文全局变量,每个请求拥有独立的命名空间,生命周期从请求开始到请求结束。

工作原理:

  1. 请求到达时,Flask创建新的请求上下文
  2. 首次调用get_db()时,创建数据库连接并存入g.db
  3. 同一请求内后续调用直接返回已有连接,实现复用
  4. 请求结束时,teardown_appcontext钩子自动关闭连接

连接管理代码:

python 复制代码
from flask import g
import sqlite3

def get_db():
    """获取数据库连接(请求级复用)"""
    if "db" not in g:
        # 首次调用:创建新连接
        g.db = sqlite3.connect(DB_PATH)
        g.db.row_factory = sqlite3.Row  # 返回字典格式
    return g.db

@app.teardown_appcontext
def close_db(exc):
    """请求结束自动关闭连接"""
    db = g.pop("db", None)
    if db is not None:
        db.close()

SQLite数据库 g对象 Flask框架 客户端 SQLite数据库 g对象 Flask框架 客户端 g.db = None g.db 已存在 HTTP请求到达 创建请求上下文 get_db() 第一次调用 sqlite3.connect() 连接对象 g.db = 连接对象 返回连接 get_db() 第二次调用 直接返回连接(复用) 执行业务逻辑... teardown_appcontext db.close() HTTP响应返回

这种设计实现了请求级连接复用,既避免了频繁创建连接的开销,又保证了线程安全。

要素 3:三层架构设计

本系统采用经典的三层架构模式:

架构分层详解:
💾 数据访问层
⚙️ 业务逻辑层
📡 路由层
@app.route()
解析URL参数
request.get_json()
validate_good()
类型转换
业务规则检查
get_db()
SQL执行
db.commit()

路由层:接收HTTP请求,解析参数,返回JSON响应

  • 定义RESTful API端点
  • 处理URL参数和请求体
  • 调用业务逻辑层处理

业务逻辑层:执行数据校验和业务规则

  • validate_good()函数校验数据完整性
  • 类型转换和范围检查
  • 错误处理和异常响应

数据访问层:管理数据库连接和SQL操作

  • get_db()获取连接
  • init_db()初始化表结构
  • close_db()自动清理资源

三层架构代码示例:

python 复制代码
from flask import Flask, request, jsonify
from datetime import datetime

app = Flask(__name__)

# ==================== 路由层 ====================
@app.route("/api/goods", methods=["POST"])
def add_good():
    """添加商品接口"""
    # 解析请求体
    data = request.get_json()
  
    # 调用业务逻辑层
    fields, err_resp, err_code = validate_good(data)
    if err_resp:
        return err_resp, err_code
  
    # 调用数据访问层
    db = get_db()
    now = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
    cursor = db.execute(
        "INSERT INTO goods (name, price, stock, category, description, created_at) VALUES (?, ?, ?, ?, ?, ?)",
        (fields["name"], fields["price"], fields["stock"], fields["category"], fields["description"], now),
    )
    db.commit()
  
    # 返回响应
    good = {"id": cursor.lastrowid, **fields, "created_at": now}
    return jsonify(good), 201

# ==================== 业务逻辑层 ====================
def validate_good(data):
    """数据校验函数"""
    name = data.get("name", "").strip()
    price = data.get("price", 0)
    stock = data.get("stock", 0)
  
    if not name:
        return None, jsonify({"error": "商品名称不能为空"}), 400
  
    try:
        price = float(price)
    except (ValueError, TypeError):
        return None, jsonify({"error": "价格格式无效"}), 400
  
    if price < 0:
        return None, jsonify({"error": "价格不能为负数"}), 400
  
    try:
        stock = int(stock)
    except (ValueError, TypeError):
        return None, jsonify({"error": "库存格式无效"}), 400
  
    if stock < 0:
        return None, jsonify({"error": "库存不能为负数"}), 400
  
    return {"name": name, "price": price, "stock": stock, 
            "category": data.get("category", "").strip(),
            "description": data.get("description", "").strip()}, None, None

# ==================== 数据访问层 ====================
def get_db():
    """获取数据库连接"""
    if "db" not in g:
        g.db = sqlite3.connect(DB_PATH)
        g.db.row_factory = sqlite3.Row
    return g.db

要素 4:数据校验机制

数据校验采用前后端双重保障策略:
不能替代
存入数据库
🔒 后端校验(数据安全)
strip()去除空格
float()/int()类型转换
业务规则检查
🖥️ 前端校验(用户体验)
HTML5 required
input type=number
实时反馈
SQLite

前端校验:提升用户体验

  • HTML5 required属性确保必填项
  • input type="number"限制数值格式
  • 实时反馈减少无效请求

后端校验:确保数据安全

  • strip()去除首尾空格
  • float()/int()类型转换捕获异常
  • 业务规则检查(非负数、非空等)
  • 白名单校验排序字段防止注入

前端校验代码示例:

html 复制代码
<form id="goodForm" onsubmit="submitForm(event)">
    <div class="form-group">
        <label>商品名称</label>
        <!-- required: HTML5必填校验 -->
        <input type="text" id="goodName" required placeholder="请输入商品名称">
    </div>
  
    <div class="form-group">
        <label>价格 (元)</label>
        <!-- type="number" + min + step: 数值格式校验 -->
        <input type="number" id="goodPrice" required step="0.01" min="0" placeholder="请输入价格">
    </div>
  
    <div class="form-group">
        <label>库存</label>
        <!-- min="0": 最小值限制 -->
        <input type="number" id="goodStock" required min="0" step="1" placeholder="库存数量">
    </div>
  
    <button type="submit" class="btn-save">保存</button>
</form>

<script>
async function submitForm(e) {
    e.preventDefault();
  
    const body = {
        name: document.getElementById("goodName").value,
        price: document.getElementById("goodPrice").value,
        stock: document.getElementById("goodStock").value,
    };
  
    const res = await fetch("/api/goods", {
        method: "POST",
        headers: { "Content-Type": "application/json" },
        body: JSON.stringify(body),
    });
  
    const data = await res.json();
    if (res.ok) {
        showToast("添加成功");
    } else {
        showToast(data.error || "操作失败");
    }
}
</script>

后端校验代码示例:

python 复制代码
def validate_good(data):
    """后端数据校验"""
    # 1. 提取并清理数据
    name = data.get("name", "").strip()  # 去除首尾空格
    price = data.get("price", 0)
    stock = data.get("stock", 0)
  
    # 2. 必填项检查
    if not name:
        return None, jsonify({"error": "商品名称不能为空"}), 400
  
    # 3. 类型转换(捕获异常)
    try:
        price = float(price)
    except (ValueError, TypeError):
        return None, jsonify({"error": "价格格式无效"}), 400
  
    # 4. 业务规则检查
    if price < 0:
        return None, jsonify({"error": "价格不能为负数"}), 400
  
    try:
        stock = int(stock)
    except (ValueError, TypeError):
        return None, jsonify({"error": "库存格式无效"}), 400
  
    if stock < 0:
        return None, jsonify({"error": "库存不能为负数"}), 400
  
    # 5. 返回清理后的数据
    return {
        "name": name,
        "price": price,
        "stock": stock,
        "category": data.get("category", "").strip(),
        "description": data.get("description", "").strip()
    }, None, None

要素 5:安全性防护

系统实现了多重安全防护:

SQL注入防护对比:

方式 代码示例 安全性
❌ 字符串拼接 sql = f"SELECT * FROM goods WHERE name='{name}'" 危险!易受攻击
✅ 参数化查询 sql = "SELECT * FROM goods WHERE name=?" 安全!自动转义

攻击场景演示:

python 复制代码
# ❌ 错误示例(易受SQL注入攻击)
name = request.get_json()["name"]
sql = f"SELECT * FROM goods WHERE name='{name}'"  # 危险!
db.execute(sql)

# 攻击场景:
# 用户输入: name = "'; DROP TABLE goods; --"
# 最终SQL: SELECT * FROM goods WHERE name=''; DROP TABLE goods; --'
# 结果: goods表被删除!

# ✅ 正确示例(参数化查询,安全)
name = request.get_json()["name"]
sql = "SELECT * FROM goods WHERE name=?"
cursor = db.execute(sql, (name,))  # 安全!SQLite自动转义

白名单校验代码:

python 复制代码
@app.route("/api/goods", methods=["GET"])
def get_goods():
    """查询商品列表"""
    db = get_db()
  
    # 获取排序参数
    sort = request.args.get("sort", "id")
    order = request.args.get("order", "asc")
  
    # ✅ 白名单校验(防止注入)
    allowed_sorts = {"id", "name", "price", "stock", "category", "created_at"}
    if sort not in allowed_sorts:
        sort = "id"  # 拒绝非法字段
  
    if order not in ("asc", "desc"):
        order = "asc"
  
    # 构建SQL(排序字段已通过白名单校验,可安全拼接)
    sql = f"SELECT * FROM goods ORDER BY {sort} {order.upper()}"
    rows = db.execute(sql).fetchall()
  
    return jsonify([dict(r) for r in rows])

安全性设计要点:

安全措施 实现方式 防护目标
参数化查询 db.execute(sql, params) SQL注入
白名单校验 if sort not in allowed_sorts 非法字段注入
数据清理 .strip(), float(), int() 非法数据
类型检查 try-except捕获异常 类型错误
范围检查 if price < 0 异常值

要素 6:RESTful API设计

接口遵循RESTful规范:

API方法详解:
🗑️ DELETE /api/goods/:id
删除商品
幂等操作
返回: 200 或 404
✏️ PUT /api/goods/:id
更新商品
URL包含ID
返回: 200 或 404
📤 POST /api/goods
添加新商品
请求体: JSON
返回: 201 + 新对象
📥 GET /api/goods
查询商品列表
支持category筛选
支持sort排序
返回: 200 + JSON数组

RESTful API完整代码实现:

python 复制代码
# 查询列表
@app.route("/api/goods", methods=["GET"])
def get_goods():
    db = get_db()
    category = request.args.get("category", "")
    sort = request.args.get("sort", "id")
    order = request.args.get("order", "asc")
  
    # 白名单校验
    allowed_sorts = {"id", "name", "price", "stock", "category", "created_at"}
    if sort not in allowed_sorts:
        sort = "id"
    if order not in ("asc", "desc"):
        order = "asc"
  
    sql = "SELECT * FROM goods"
    params = []
    if category:
        sql += " WHERE category=?"
        params.append(category)
    sql += f" ORDER BY {sort} {order.upper()}"
  
    rows = db.execute(sql, params).fetchall()
    return jsonify([dict(r) for r in rows])

# 添加商品
@app.route("/api/goods", methods=["POST"])
def add_good():
    data = request.get_json()
    fields, err_resp, err_code = validate_good(data)
    if err_resp:
        return err_resp, err_code
  
    db = get_db()
    now = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
    cursor = db.execute(
        "INSERT INTO goods (name, price, stock, category, description, created_at) VALUES (?, ?, ?, ?, ?, ?)",
        (fields["name"], fields["price"], fields["stock"], fields["category"], fields["description"], now),
    )
    db.commit()
    good = {"id": cursor.lastrowid, **fields, "created_at": now}
    return jsonify(good), 201  # 201 Created

# 更新商品
@app.route("/api/goods/<int:good_id>", methods=["PUT"])
def update_good(good_id):
    data = request.get_json()
    fields, err_resp, err_code = validate_good(data)
    if err_resp:
        return err_resp, err_code
  
    db = get_db()
    cursor = db.execute(
        "UPDATE goods SET name=?, price=?, stock=?, category=?, description=? WHERE id=?",
        (fields["name"], fields["price"], fields["stock"], fields["category"], fields["description"], good_id),
    )
    db.commit()
  
    if cursor.rowcount == 0:
        return jsonify({"error": "商品不存在"}), 404
  
    return jsonify({"id": good_id, **fields})

# 删除商品
@app.route("/api/goods/<int:good_id>", methods=["DELETE"])
def delete_good(good_id):
    db = get_db()
    cursor = db.execute("DELETE FROM goods WHERE id=?", (good_id,))
    db.commit()
  
    if cursor.rowcount == 0:
        return jsonify({"error": "商品不存在"}), 404
  
    return jsonify({"message": "删除成功"})

API接口文档:

方法 路径 说明 请求体 响应码
GET /api/goods 查询列表 - 200
POST /api/goods 添加商品 JSON 201
PUT /api/goods/:id 更新商品 JSON 200/404
DELETE /api/goods/:id 删除商品 - 200/404
GET /api/stats 统计信息 - 200

实施路径

第一阶段 数据库初始化 定义goods.db路径 CREATE TABLE建表 PRAGMA检测字段 ALTER TABLE迁移 第二阶段 连接管理 get_db()函数 g对象存储连接 teardown钩子 自动清理资源 第三阶段 路由与业务 @app.route定义 validate_good校验 SQL操作执行 JSON响应返回 第四阶段 前端集成 fetch API请求 动态渲染页面 Toast交互反馈 错误处理提示 实施路径时间线

第一阶段:数据库初始化

  1. 定义数据库文件路径(goods.db)
  2. 创建表结构,设置字段类型和约束
  3. 使用IF NOT EXISTS确保幂等性
  4. 通过PRAGMA table_info检测已有字段
  5. 动态ALTER TABLE支持字段扩展

第二阶段:连接管理实现

  1. 定义get_db()函数获取连接
  2. 使用g对象存储连接实例
  3. 设置row_factory为sqlite3.Row
  4. 注册teardown_appcontext钩子
  5. 实现自动关闭和资源清理

第三阶段:路由与业务逻辑

  1. 定义路由装饰器和HTTP方法
  2. 解析请求参数和JSON数据
  3. 调用validate_good()校验数据
  4. 执行SQL操作并提交事务
  5. 构造JSON响应返回结果

第四阶段:前端集成

  1. 使用fetch API发送请求
  2. 处理响应数据和错误信息
  3. 实现页面动态渲染
  4. 添加交互反馈(Toast提示)
  5. 处理加载状态和空数据展示

总结YXX

Flask+SQLite简易后端数据存储方案以其零配置、单文件、轻量级的特性,为小型项目开发提供了优雅的解决方案。通过g对象实现请求级连接管理,既保证了性能又确保了线程安全;通过参数化查询和白名单校验有效防范SQL注入;通过三层架构实现职责分离和代码复用。

这套方案虽然简单,但包含了后端开发的核心理念:数据持久化、请求处理、安全防护、架构设计。对于初学者而言,是理解Web后端原理的最佳起点;对于有经验的开发者,是快速原型验证的利器。

当项目规模增长时,可以平滑升级到MySQL/PostgreSQL,引入SQLAlchemy ORM,采用Gunicorn+Nginx部署,但核心原理和设计思想始终不变。这正体现了"先学会走,再学会跑"的渐进式学习路径。


小型项目

Flask+SQLite
规模增长?
中型项目

+MySQL/PostgreSQL
大型项目

+SQLAlchemy ORM
生产环境

+Gunicorn+Nginx

相关推荐
Dicky-_-zhang10 小时前
Redis集群模式详解与实战配置
java·jvm
Kiling_070410 小时前
面向对象和集合编程题 ( 一 )
jvm·windows
ZC跨境爬虫10 小时前
模块化烹饪小程序开发日记 Day5:(后端Flask接口开发与AI智能解析菜谱的实现)
前端·人工智能·后端·python·ui·flask
上弦月-编程10 小时前
Java类与对象:编程核心解密
java·开发语言·jvm
Dicky-_-zhang10 小时前
线上故障排查与应急响应实战:从零开始建立你的SRE体系
java·jvm
大大杰哥10 小时前
从 Volatile 到 ThreadLocal:Java 线程安全机制备忘
java·开发语言·jvm
AI人工智能+电脑小能手11 小时前
【大白话说Java面试题 第67题】【JVM篇】第27题:生产环境服务器变慢,诊断思路和性能评估谈谈?
java·服务器·jvm·面试
Dicky-_-zhang11 小时前
服务网格Istio mTLS配置实战
java·jvm
码界筑梦坊11 小时前
130-基于Python的体育用品销售数据可视化分析系统
开发语言·python·信息可视化·flask·毕业设计