在Python生态中操作SQLite有很多选择,从内置的sqlite3标准库到功能完整的ORM框架。SQLAlchemy ORM的价值在于把关系型数据库的表结构映射为Python类,让开发者用对象属性的方式读写数据,避免在业务代码中散落大量SQL字符串。本文以SQLite为目标数据库,从环境准备、模型定义到增删改查和事务控制,完整演示SQLAlchemy ORM的常用操作。

SQLite无需独立服务器进程,数据库就是一个文件,非常适合桌面应用、小型服务和单元测试场景。接下来先安装依赖并创建第一个引擎,然后逐步展开模型映射和会话操作。
一、环境准备与SQLite引擎
使用SQLAlchemy前需要先安装对应模块。可以通过pip安装SQLAlchemy,安装命令为pip install sqlalchemy。如果项目使用虚拟环境,建议在虚拟环境内执行。当前SQLAlchemy 2.x版本提供统一的API风格,与早期1.x版本略有差异,本文代码以2.x风格为主。
创建SQLite引擎的入口是create_engine函数。SQLite连接串通常写成sqlite:///数据库文件路径,开头三个斜杠表示相对路径,例如sqlite:///example.db会在当前工作目录生成或打开example.db文件。如果希望使用内存数据库,可以写成sqlite://或sqlite:///:memory:。内存数据库在连接关闭后数据会消失,主要用于快速测试。
from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker
engine = create_engine("sqlite:///example.db", echo=False)
SessionLocal = sessionmaker(bind=engine, autoflush=False, autocommit=False)
上面的代码先创建引擎,再通过sessionmaker生成一个会话工厂。参数echo=False表示不打印SQL语句,调试时可以改为True查看实际执行的SQL。autoflush=False和autocommit=False让事务边界更加明确,避免隐式刷新和自动提交造成困惑。
SQLite连接默认会检查是否在同一线程使用连接,如果希望跨线程共享连接,可以在create_engine中通过connect_args传入{"check_same_thread": False}。这个参数适合在测试或多线程工具中配合连接池使用,但生产环境仍然要关注SQLite的文件锁限制。
二、ORM模型定义与自动建表
ORM模型需要继承一个声明基类。通过declarative_base创建基类后,每个数据库表对应一个Python类,类属性对应字段。相比手写SQL建表语句,模型定义能集中管理字段类型、约束和索引,也更方便IDE补全和重构。
from sqlalchemy import Column, Integer, String, ForeignKey
from sqlalchemy.orm import declarative_base, relationship
Base = declarative_base()
class User(Base):
__tablename__ = "users"
id = Column(Integer, primary_key=True)
name = Column(String(50), nullable=False)
email = Column(String(120), unique=True, index=True)
posts = relationship("Post", back_populates="author")
class Post(Base):
__tablename__ = "posts"
id = Column(Integer, primary_key=True)
title = Column(String(200), nullable=False)
content = Column(String)
user_id = Column(Integer, ForeignKey("users.id"))
author = relationship("User", back_populates="posts")
上面定义了两个表:users和posts。User类中的posts与Post类中的author通过relationship建立一对多关系。ForeignKey("users.id")指定外键约束,back_populates让双向访问保持同步。查询操作虽然可以用join,但关系属性让对象图导航更加直观。
模型定义完成后,需要调用Base.metadata.create_all(engine)来实际创建表。该方法只会创建不存在的表,不会修改已有表结构。所以在开发早期,模型变更后如果字段不匹配,可能需要删除旧数据库文件或者使用迁移工具。SQLite本身对ALTER TABLE支持有限,后续引入Alembic做迁移会更稳妥。
字段类型方面,SQLAlchemy会根据Column声明自动映射到SQLite的存储类型。String对应TEXT,Integer对应INTEGER,Float对应REAL,Boolean在SQLite中通常存储为0或1。显式声明类型能减少默认推断的歧义,也有利于跨数据库迁移。
三、增删改查与查询过滤
写完模型后,使用SessionLocal创建会话实例,所有持久化操作都通过会话完成。新增一条记录时,先创建模型对象,再调用session.add,最后执行session.commit提交事务。如果没有commit,当前事务内可以查询到该对象,但数据库文件中不会真正写入。
session = SessionLocal() new_user = User(name="张三", email="zhangsan@ipipp.com") session.add(new_user) session.commit() post = Post(title="第一篇", content="SQLAlchemy ORM操作SQLite", author=new_user) session.add(post) session.commit()
上面的代码先创建User并提交,再创建一个Post并直接通过author关系属性关联到已有用户。提交后可以通过session.refresh(new_user)重新加载数据库中的最新状态。注意每次commit后如果需要继续使用对象,要保证会话仍然有效。
查询操作使用session.query(Model),可以通过filter和filter_by添加条件。filter接收SQL表达式,适合复杂条件;filter_by使用关键字参数,适合简单等值过滤。例如查询所有邮箱属于ipipp.com的用户,可以使用filter(User.email.like("%ipipp.com"))。order_by可以排序,limit和offset可以分页。
users = session.query(User).filter(User.email.like("%ipipp.com")).all()
first_user = session.query(User).filter_by(name="张三").first()
session.query(Post).filter(Post.user_id == first_user.id).delete()
session.commit()
更新操作通常先查询出对象,然后直接修改属性值,最后commit。ORM会追踪对象的脏数据状态,只对发生变化的字段生成UPDATE语句。删除操作可以调用session.delete(obj),也可以使用Query的delete方法进行批量删除,但批量删除默认不触发对象级事件,需要谨慎使用。上面的例子中通过filter(Post.user_id == first_user.id)删除指定用户的所有文章。
使用完毕后应调用session.close释放连接。推荐使用with SessionLocal() as session:上下文管理,在代码块结束时自动关闭会话,减少连接泄漏风险。
四、事务管理与SQLite并发注意点
SQLAlchemy会话的事务默认由commit和rollback控制。一次commit会提交当前事务,并开启下一个事务。如果在操作过程中发生异常,应该调用session.rollback回滚当前事务,否则会话可能处于异常状态。更推荐的写法是用session.begin上下文:
with SessionLocal() as session:
with session.begin():
user = User(name="李四", email="lisi@ipipp.com")
session.add(user)
# 如果这里抛出异常,整个事务自动回滚
使用session.begin后,代码块正常结束会自动提交,发生异常会自动回滚,避免遗漏。这种方式在处理多表插入和关联数据时尤其有用,能保证数据一致性。
SQLite在并发写入方面比客户端服务器型数据库要弱一些。默认情况下,写操作会锁定整个数据库文件,其他写操作必须等待。如果多个线程或进程同时写入,容易出现database is locked错误。可以通过设置合理的超时时间缓解,例如在创建引擎时添加connect_args={"timeout": 15},让SQLite等待最多15秒获取写锁。
对于读多写少的场景,可以开启SQLite的WAL模式。WAL允许读写并发,写入先记录到预写日志,再由checkpoint合并到主文件。在SQLAlchemy中可以通过事件监听在连接建立后执行PRAGMA journal_mode=WAL;来开启。不过WAL模式会生成额外的-wal和-shm文件,需要确保备份策略考虑到这些文件。
SQLAlchemy ORM操作SQLite的完整流程可以总结为:定义模型、创建引擎和会话、执行增删改查、控制事务边界。理解Session的生命周期和SQLite的锁机制后,就能在小项目中可靠地使用这套组合。对于大型应用,建议配合迁移工具和连接池参数进一步优化。

