SQLAlchemy ORM 数据库操作指南
SQLAlchemy 是 Python 中广受欢迎的对象关系映射 (ORM) 工具,它为数据库交互提供了一种高效且富有弹性的方法。本指南将深入探讨如何运用 SQLAlchemy ORM 来管理您的数据库操作。
安装
首先,使用 pip 来安装 SQLAlchemy:
pip install sqlalchemy
如果您需要连接特定的数据库系统,请额外安装相应的数据库驱动程序:
# 用于连接 PostgreSQL
pip install psycopg2-binary
# 用于连接 MySQL
pip install mysql-connector-python
# SQLite 数据库驱动已包含在 Python 标准库中,无需额外安装
核心组件解析
- Engine:数据库引擎,是 SQLAlchemy 与数据库通信的枢纽。
- Session:数据库会话,负责跟踪和管理所有的持久化数据库操作。
- Model:数据模型类,用于映射数据库中的表结构。
- Query:查询对象,专门用于构造和执行数据库查询语句。
建立数据库连接
使用 create_engine 函数来初始化数据库连接:
from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker
# 初始化数据库引擎
# SQLite 示例
db_engine = create_engine('sqlite:///data.db', echo=True)
# PostgreSQL 连接配置示例
# db_engine = create_engine('postgresql://user:secret@host:port/dbname')
# MySQL 连接配置示例
# db_engine = create_engine('mysql+mysqlconnector://user:secret@host:port/dbname')
# 创建会话工厂
SessionProvider = sessionmaker(autocommit=False, autoflush=False, bind=db_engine)
# 实例化数据库会话
current_session = SessionProvider()
定义数据模型
通过继承 declarative_base 创建的基类,定义您的数据模型类:
from sqlalchemy import Column, Integer, String, ForeignKey
from sqlalchemy.orm import relationship, declarative_base
# 设置所有模型的基类
BaseModel = declarative_base()
class Member(BaseModel):
__tablename__ = 'members' # 表名
member_id = Column('id', Integer, primary_key=True, index=True)
username = Column(String(64), nullable=False, index=True)
email_address = Column(String(120), unique=True, index=True)
# 定义与 Article 模型的一对多关系
articles = relationship("Article", back_populates="author")
class Article(BaseModel):
__tablename__ = 'articles'
article_id = Column('id', Integer, primary_key=True, index=True)
article_title = Column(String(255), nullable=False)
content_body = Column(String(1000))
author_ref_id = Column(Integer, ForeignKey('members.id')) # 外键关联到 Member
# 定义与 Member 模型的多对一关系
author = relationship("Member", back_populates="articles")
# 定义多对多关系,需要一个中间表
categories = relationship("Category", secondary="article_categories", back_populates="associated_articles")
class Category(BaseModel):
__tablename__ = 'categories'
category_id = Column('id', Integer, primary_key=True, index=True)
category_name = Column(String(50), unique=True, nullable=False)
associated_articles = relationship("Article", secondary="article_categories", back_populates="categories")
# 中间表,用于实现 Article 和 Category 的多对多关系
class ArticleCategoryLink(BaseModel):
__tablename__ = 'article_categories'
post_id = Column(Integer, ForeignKey('articles.id'), primary_key=True)
category_id = Column(Integer, ForeignKey('categories.id'), primary_key=True)
创建数据库表结构
使用 BaseModel.metadata.create_all 来根据模型定义创建数据库表:
# 创建所有定义的表
BaseModel.metadata.create_all(bind=db_engine)
# 如需删除所有表,可取消注释以下行
# BaseModel.metadata.drop_all(bind=db_engine)
基本数据操作 (CRUD)
插入记录
# 创建一个新成员
new_member = Member(username="Alice", email_address="alice@example.com")
current_session.add(new_member)
current_session.commit() # 提交事务以保存更改
# 批量插入
current_session.add_all([
Member(username="Bob", email_address="bob@example.com"),
Member(username="Charlie", email_address="charlie@example.com")
])
current_session.commit()
查询记录
# 获取所有成员
all_members = current_session.query(Member).all()
# 获取第一条记录
first_member = current_session.query(Member).first()
# 根据主键 ID 获取成员
member_by_id = current_session.query(Member).get(1)
更新记录
# 查询并修改
member_to_update = current_session.query(Member).get(1)
if member_to_update:
member_to_update.username = "Alice Smith"
current_session.commit()
# 批量更新,注意 synchronize_session=False 避免潜在问题
current_session.query(Member).filter(Member.username.like("B%")).update({"username": "B. User"}, synchronize_session=False)
current_session.commit()
删除记录
# 查询并删除
member_to_delete = current_session.query(Member).get(1)
if member_to_delete:
current_session.delete(member_to_delete)
current_session.commit()
# 批量删除
current_session.query(Member).filter(Member.username == "Charlie").delete(synchronize_session=False)
current_session.commit()
高级查询技巧
条件过滤
from sqlalchemy import or_
# 等值查询
specific_member = current_session.query(Member).filter(Member.username == "Bob").first()
# LIKE 查询
members_like_b = current_session.query(Member).filter(Member.username.like("B%")).all()
# IN 查询
members_in_list = current_session.query(Member).filter(Member.username.in_(["Alice", "Bob"])).all()
# 组合条件 AND
complex_filter = current_session.query(Member).filter(
Member.username == "Alice",
Member.email_address.endswith("@example.com")
).all()
# 组合条件 OR
or_filter = current_session.query(Member).filter(
or_(Member.username == "Alice", Member.username == "Bob")
).all()
# 不等于
not_equal_filter = current_session.query(Member).filter(Member.username != "Charlie").all()
排序、限制与偏移
# 按用户名降序排序
sorted_members = current_session.query(Member).order_by(Member.username.desc()).all()
# 限制返回数量
limited_members = current_session.query(Member).limit(5).all()
# 跳过指定数量并限制
paginated_members = current_session.query(Member).offset(10).limit(5).all()
聚合函数
from sqlalchemy import func
# 统计总数
total_members = current_session.query(Member).count()
# 按用户名分组统计文章数量
member_article_counts = current_session.query(
Member.username,
func.count(Article.article_id)
).join(Article).group_by(Member.username).all()
# 计算平均 ID
average_member_id = current_session.query(func.avg(Member.member_id)).scalar()
JOIN 操作
# 内连接查询 User 和 Article
user_article_list = current_session.query(Member, Article).join(Article).filter(Article.article_title.like("%Python%")).all()
# 左外连接
outer_join_list = current_session.query(Member, Article).outerjoin(Article).all()
# 指定连接条件
explicit_join = current_session.query(Member, Article).join(Article, Member.member_id == Article.author_ref_id).all()
关系数据操作
SQLAlchemy 使得处理模型之间的关联变得直观。
# 创建关联对象
new_member = Member(username="David", email_address="david@example.com")
new_article = Article(article_title="My First Post", content_body="Content here.", author=new_member)
current_session.add(new_article)
current_session.commit()
# 通过关系访问关联对象
print(f"文章 '{new_article.article_title}' 的作者是: {new_article.author.username}")
print(f"成员 {new_member.username} 的所有文章:")
for art in new_member.articles:
print(f" - {art.article_title}")
# 多对多关系操作
python_cat = Category(category_name="Python")
web_dev_cat = Category(category_name="Web Development")
new_article.categories.append(python_cat)
new_article.categories.append(web_dev_cat)
current_session.commit()
print(f"文章 '{new_article.article_title}' 的分类:")
for cat in new_article.categories:
print(f" - {cat.category_name}")
事务管理
SQLAlchemy 提供了健壮的事务处理机制。
# 自动提交机制与异常处理
try:
temp_member = Member(username="Temp User", email_address="temp@example.com")
current_session.add(temp_member)
current_session.commit()
except Exception as error:
current_session.rollback() # 发生错误时回滚
print(f"操作失败: {error}")
# 使用事务上下文管理器 (推荐)
from sqlalchemy.orm import Session
def add_new_member(db_session: Session, name: str, email: str):
try:
member = Member(username=name, email_address=email)
db_session.add(member)
db_session.commit()
return member
except Exception:
db_session.rollback()
raise
# 嵌套事务
with current_session.begin_nested():
# 在这里执行一系列操作,若内部失败,则仅回滚此块
member1 = Member(username="Nested1", email_address="nested1@example.com")
current_session.add(member1)
# 保存点 (Savepoint)
save_point = current_session.begin_nested()
try:
member2 = Member(username="Savepoint", email_address="save@example.com")
current_session.add(member2)
save_point.commit() # 提交保存点
except Exception:
save_point.rollback() # 回滚到保存点
开发最佳实践
- 会话生命周期:为每个独立的操作(如 Web 请求)管理一个独立的会话,并在操作完成后及时关闭。
- 错误处理:总是包含 try...except 块来捕获异常,并调用
session.rollback()。 - 优化查询:警惕 N+1 查询问题,利用
joinedload或selectinload等进行预加载 (Eager Loading)。 - 连接池配置:合理设置数据库连接池的大小和超时时间。
- 数据校验:在数据存入数据库前,进行严格的输入校验。
使用上下文管理器可以简化会话的管理:
from contextlib import contextmanager
@contextmanager
def manage_db_session():
db = SessionProvider() # 使用之前创建的会话工厂
try:
yield db
db.commit() # 成功则提交
except Exception:
db.rollback() # 失败则回滚
raise
finally:
db.close() # 总是关闭会话
# 使用示例
with manage_db_session() as session:
new_member = Member(username="Context User", email_address="context@example.com")
session.add(new_member)
# 事务在 with 块结束时自动提交或回滚