导读:本期聚焦于杨子江创作的《Flask-SQLAlchemy操作SQLite数据库怎么做?从建表到增删改查全流程详解》,敬请观看详情。当一个Python Web项目只需要轻量级数据存储时,SQLite几乎是零成本的首选,而Flask-SQLAlchemy则把ORM的操作体验带到了另一个层次。本文围绕Flask-SQLAlchemy配合SQLite的完整使用流程展开,先讲清环境安装与项目配置,再通过图书管理的例子演示如何定义模型、自动建表,随后逐一拆解增删改查的写法,包括单条插入、批量提交、条件过滤、分页查询以及事务回滚。文章还整理了开发中常踩的坑,比如数据库文件路径配置、多线程访问报错、外键约束默认关闭等问题,并给出对应的解决代码。看完这篇内容,你可以独立搭建一套基于SQLite的Flask后端数据层,并把这套模式直接迁移到自己的项目中。

Flask项目做到需要持久化数据的那一步,很多问题就来了:要不要单独装一个MySQL?测试环境数据库怎么搞?其实对于中小型项目、个人工具或者原型验证,SQLite配合Flask-SQLAlchemy完全够用,不需要安装数据库服务,一个文件就是一整个库,部署和备份都极其简单。这篇文章就从零开始,把配置、建模、增删改查到常见坑位处理完整过一遍,代码可以直接复制运行。

Flask-SQLAlchemy操作SQLite数据库怎么做?从建表到增删改查全流程详解

环境准备与数据库配置

先把依赖装好。建议在虚拟环境里操作,避免污染全局包:

pip install flask flask-sqlalchemy

Flask-SQLAlchemy从3.x版本开始要求显式创建SQLAlchemy实例,推荐写法是先建一个全局的db对象,再在应用工厂函数里完成绑定。这种结构方便后续拆分蓝图和模型文件。

接着配置数据库连接。SQLite的连接字符串以sqlite:///开头,注意三个斜杠表示相对路径,四个斜杠表示绝对路径。写错路径是新手最常见的问题之一,表面上看程序不报错,实际数据库文件被建到了你意想不到的目录。

from flask import Flask
from flask_sqlalchemy import SQLAlchemy

db = SQLAlchemy()

def create_app():
    app = Flask(__name__)
    # 相对路径:在项目根目录生成 app.db 文件
    app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///app.db'
    # 关闭不必要的修改追踪,节省内存
    app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False
    db.init_app(app)
    return app

这里有个细节要说明:sqlite:///app.db是相对路径,实际位置取决于启动脚本的工作目录。如果想固定位置,可以基于文件路径拼接:sqlite:////' + os.path.join(basedir, 'app.db'),用os.path.abspath(__file__)拿到项目根目录,这样无论从哪里启动程序,数据库文件位置都不会漂移。

定义模型并自动建表

ORM的核心思路是把数据库表映射成Python类,表中的一行对应一个对象。下面用一个图书管理的场景来演示,包含一张分类表和一张图书表,两者通过外键关联。

class Category(db.Model):
    __tablename__ = 'categories'
    id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.String(50), unique=True, nullable=False)
    books = db.relationship('Book', backref='category', lazy=True)

class Book(db.Model):
    __tablename__ = 'books'
    id = db.Column(db.Integer, primary_key=True)
    title = db.Column(db.String(100), nullable=False)
    author = db.Column(db.String(50))
    price = db.Column(db.Float, default=0)
    category_id = db.Column(db.Integer, db.ForeignKey('categories.id'))

几个字段属性值得注意:nullable=False对应数据库的NOT NULL约束;unique=True保证字段值不重复;default是Python层面的默认值,插入时如果没传就自动填充。db.relationship建立的是ORM层面的关联,通过backref参数,可以直接用book.category拿到分类对象,也可以用category.books反向查出该分类下所有图书,不用手写join。

模型定义好后,建表只需要两行代码。可以在命令行里手动触发,也可以写成脚本:

from yourapp import create_app, db

app = create_app()
with app.app_context():
    db.create_all()  # 只创建不存在的表,不会覆盖已有数据

create_all的行为是安全的:已经存在的表会被跳过,不会清空数据。但这也意味着它不能帮你修改表结构,如果字段变了,要么删掉db文件重来,要么引入Alembic做迁移管理。生产项目建议一开始就用Flask-Migrate,避免后期手工改表的麻烦。

增删改查的完整写法

先看插入数据。ORM的写法是创建对象、加入会话、提交事务三步:

from yourapp import create_app, db
from yourapp.models import Category, Book

app = create_app()
with app.app_context():
    # 单条插入
    cat = Category(name='编程')
    db.session.add(cat)
    db.session.commit()  # 提交后 cat.id 才有值

    # 批量插入
    books = [
        Book(title='Python基础教程', author='张三', price=79.0, category_id=cat.id),
        Book(title='Flask实战', author='李四', price=89.0, category_id=cat.id),
    ]
    db.session.add_all(books)
    db.session.commit()

必须强调一点:add之后数据并没有真正写入数据库,只有执行commit才会落盘。如果中途出错,用db.session.rollback()回滚,会话会恢复到事务开始前的状态。建议在视图函数里写try-except,异常时先回滚再抛出,避免会话残留脏数据影响下一次请求。

查询是日常用得最多的部分。常用的过滤方法有两种:filter_by接收关键字参数,写法简单;filter接收表达式,支持更灵活的比较运算。

with app.app_context():
    # 按主键查询,查不到返回 None
    book = Book.query.get(1)

    # 等值过滤
    cheap = Book.query.filter_by(author='张三').all()

    # 条件表达式:价格区间 + 排序 + 分页
    result = (Book.query
              .filter(Book.price >= 50, Book.price <= 100)
              .order_by(Book.price.desc())
              .paginate(page=1, per_page=10))
    print(result.items)   # 当前页数据
    print(result.total)   # 总条数

    # 模糊搜索
    like = Book.query.filter(Book.title.like('%Python%')).all()

    # 连同关联对象一起统计每个分类的书数
    for c in Category.query.all():
        print(c.name, c.books.count() if c.books else 0)

get方法只适用于主键查询,Flask-SQLAlchemy 3.x中它不会自动抛出404,需要自己判空。paginate是做列表页的利器,返回的对象包含itemstotalpages等属性,分页逻辑不需要手写SQL的LIMIT和OFFSET。

更新和删除相对简单,但删除时留意级联问题:

with app.app_context():
    # 更新:先查出来再改属性
    book = Book.query.filter_by(title='Flask实战').first()
    if book:
        book.price = 69.0
        db.session.commit()

    # 删除
    target = Book.query.get(2)
    if target:
        db.session.delete(target)
        db.session.commit()

常见坑与解决方案

第一个坑是外键不生效。SQLite出于历史兼容原因,默认关闭外键约束,也就是说删除分类后,图书表的category_id仍指向一个不存在的id,数据完整性被破坏。解决方式是在连接参数里打开外键开关:

app.config['SQLALCHEMY_DATABASE_URI'] = (
    'sqlite:///app.db?'
    'check_same_thread=false&foreign_keys=on'
)

或者在模型层面配置级联删除,把db.relationship改成db.relationship('Book', backref='category', cascade='all, delete-orphan'),这样删除分类时会连带删掉其下所有图书,两种方式按业务需求选择。

第二个坑是多线程访问报错。默认情况下SQLite的连接只能在创建它的线程中使用,Flask多线程模式下会抛出sqlite3.ProgrammingError。上面的check_same_thread=false参数就是为这个准备的,同时配合连接池参数SQLALCHEMY_ENGINE_OPTIONS设置pool_sizeconnect_args,可以有效缓解并发冲突。不过要认清定位:SQLite的写入是库级锁,高并发写场景还是老老实实换PostgreSQL或MySQL。

第三个坑是配置改动不生效。改了URI之后发现程序还在连旧库,大概率是db文件路径写错了,或者create_all在错误的工作目录下执行生成了一个新的空库。排查办法是在代码里打印app.config['SQLALCHEMY_DATABASE_URI'],并用db.engine确认实际连接目标。

掌握这套流程后,你会发现SQLite加Flask-SQLAlchemy的组合非常适合快速搭建原型、写爬虫数据存储或者做个人小工具。等业务量真正上来,只需要把SQLALCHEMY_DATABASE_URI换成MySQL或PostgreSQL的连接串,模型和业务代码几乎不用动,这也是ORM带来的最大好处:底层数据库可替换,上层逻辑保持稳定。

Flask-SQLAlchemySQLitePython ORM修改时间:2026-09-15 15:12:45

免责声明:已尽一切努力确保本网站所含信息的准确性。网站作品多为原创整理与精心创作,观点力求客观中立。本站旨在免费分享,内容仅供个人学习、研究或参考使用。若引用了第三方作品,版权归原作者所有。如内容涉及您的权益,请联系我们进行处理Email:chomcom@qq.com。
引用或转载本作品时,请注明当前出处:https://www.ipipp.com/html/20260915/57350.html,基于非商业用途的前提下,欢迎转载或二创本作品。
内容垂直聚焦
专注技术核心技术栏目,确保每篇文章深度聚焦于实用技能。从代码技巧到架构设计,为用户提供无干扰的纯技术知识沉淀,精准满足专业提升需求。
知识结构清晰
覆盖从开发到部署的全链路。AI、前端、编程、数据库、服务器、建站、系统层层递进,构建清晰学习路径,帮助用户系统化掌握开发与运维所需的核心技术。
深度技术解析
拒绝泛泛而谈,深入技术细节与实践难点。无论是数据库优化还是服务器配置,均结合真实场景与代码示例进行剖析,致力于提供可直接应用于工作的解决方案。
专业领域覆盖
精准对应开发生命周期。从前端界面到后端编程,从数据库操作到服务器运维,形成完整闭环,一站式满足全栈工程师和运维人员的技术需求。
即学即用高效
内容强调实操性,步骤清晰、代码完整。用户可根据教程直接复现和应用于自身项目,显著缩短从学习到实践的距离,快速解决开发中的具体问题。
持续更新保障
专注既定技术方向进行长期、稳定的内容输出。确保各栏目技术文章持续更新迭代,紧跟主流技术发展趋势,为用户提供经久不衰的学习价值。