当前位置:首页 > 技术 > 正文内容

SQLAlchemy ORM 数据库操作指南

访客 技术 2026年7月19日 3

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 查询问题,利用 joinedloadselectinload 等进行预加载 (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 块结束时自动提交或回滚
    

相关文章

Linux crontab 详解

1) crontab 是什么cron 是 Linux 的定时任务守护进程;crontab 是用来编辑/查看“按时间周期执行命令”的表(cron table)。常见两类:用户 crontab:每个用户一份(crontab -e 编辑)系统级 crontab / cron.d:可指定执行用户(/etc/crontab、/etc/cron.d/*)2) crontab 时间...

富文本里可以允许的 HTML 属性

一、所有标签默认允许的安全属性(极少)class        (可选)id           (通常建议禁用)title️ 注意:id 容易被滥用做锚点注入,很多系统直接禁用class 允许的话最好只允许固定前缀(如 editor-*)二、a 标签允许属性<a href="" t...

Mac 安装 Node.js 指南

方法一:通过官网安装包(最简单,适合初学者)如果你只是想快速安装并开始使用,这是最直接的方法。访问 Node.js 官网。页面会显示两个版本:LTS (Recommended For Most Users):长期支持版,最稳定。建议选这个。Current:最新特性版,包含最新功能但可能不够稳定。下载 .pkg 安装包并运行。按照安装向导点击“下一步”即可完成。方法二:使用 Homebrew 安装(...

Dom\HTML_NO_DEFAULT_NS 的副作用:自动加闭合标签

在使用Dom\HTMLDocument时,Dom\HTML_NO_DEFAULT_NS 将禁止在解析过程中设置元素的命名空间, 此设置是为了与DOMDocument向后兼容而存在的。当使用它时,已知的一个副作用就是:自动加闭合标签例如 </img> 为什么会这样?当你使用:Dom\HTML_NO_DEFAULT_NS文档会变成 无命名空间模式,此时内部更接近 XML...

Laravel 事件和监听器创建

在 Laravel 中,使用 Artisan 命令创建 Events(事件) 和 Listeners(监听器) 是非常高效的。你可以通过以下几种方式来实现:1. 手动创建单个 Event如果你只想创建一个事件类,可以使用 make:event 命令:Bashphp artisan make:event UserRegistered执行后,文件将生成在 app/Even...

自定义域名解析神器 dnsmasq

什么是 dnsmasq?dnsmasq 是一个轻量级、功能强大的网络服务工具,专为小型和中等规模网络设计。它是一个综合的网络基础设施解决方案[1]。dnsmasq 能做什么?功能说明应用场景DNS 转发与缓存将 DNS 查询转发到上游服务器(ISP、Google DNS 等),并在本地缓存结果加快 DNS 查询速度,减少外部 DNS 流量本地 DNS解析本地网络设备的主机名,无需编辑&n...

发表评论

访客

◎欢迎参与讨论,请在这里发表您的看法和观点。