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

深入解析 Python SQLAlchemy ORM 的核心用法与实践

访客 技术 2026年6月22日 1

环境配置与依赖安装

在开始之前,需要通过包管理工具安装 SQLAlchemy 核心库。若需对接特定的关系型数据库,还需引入对应的底层驱动。

# 安装核心库
pip install SQLAlchemy

# 按需安装数据库驱动
# 适配 PostgreSQL
pip install psycopg2-binary

# 适配 MySQL
pip install PyMySQL

# SQLite 为 Python 内置模块,无需额外操作

核心架构组件

  • Engine(引擎):应用与数据库交互的起点,负责管理连接池和方言(Dialect)。
  • Session(会话):充当对象与数据库记录之间的中介,处理工作单元(Unit of Work)和事务。
  • Declarative Base(声明式基类):用于将 Python 类映射为数据库表的基类。
  • Query(查询构造器):用于生成 SQL 语句并提取结果集的对象。

初始化引擎与会话工厂

通过配置连接字符串来实例化引擎,并基于该引擎绑定会话工厂。

from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker

# 配置 SQLite 内存数据库或本地文件
db_url = "sqlite:///./app_data.db"
# 若使用 PostgreSQL: "postgresql+psycopg2://user:pwd@localhost:5432/dbname"

db_engine = create_engine(db_url, echo=False, pool_pre_ping=True)

# 绑定引擎并配置会话行为
SessionFactory = sessionmaker(bind=db_engine, autocommit=False, autoflush=False)

构建 ORM 数据模型

以下示例定义了作者(Author)、书籍(Book)以及分类(Category)之间的关联关系,涵盖了一对多与多对多场景。

from sqlalchemy import Column, Integer, String, ForeignKey, Table
from sqlalchemy.orm import relationship, declarative_base

ModelBase = declarative_base()

# 定义多对多关系的中间表
book_category_link = Table(
    'book_category_link', ModelBase.metadata,
    Column('book_id', Integer, ForeignKey('books.id'), primary_key=True),
    Column('category_id', Integer, ForeignKey('categories.id'), primary_key=True)
)

class Author(ModelBase):
    __tablename__ = 'authors'
    
    author_id = Column(Integer, primary_key=True, autoincrement=True)
    full_name = Column(String(64), nullable=False)
    contact_email = Column(String(128), unique=True)
    
    # 一对多:一个作者有多本书
    written_books = relationship("Book", back_populates="writer", cascade="all, delete-orphan")

class Book(ModelBase):
    __tablename__ = 'books'
    
    book_id = Column(Integer, primary_key=True, autoincrement=True)
    title = Column(String(128), nullable=False)
    abstract = Column(String(512))
    writer_id = Column(Integer, ForeignKey('authors.author_id'))
    
    writer = relationship("Author", back_populates="written_books")
    # 多对多:一本书属于多个分类
    categories = relationship("Category", secondary=book_category_link, back_populates="books")

class Category(ModelBase):
    __tablename__ = 'categories'
    
    category_id = Column(Integer, primary_key=True, autoincrement=True)
    label = Column(String(32), unique=True, nullable=False)
    
    books = relationship("Book", secondary=book_category_link, back_populates="categories")

数据表结构同步

利用元数据对象将 Python 模型结构同步至物理数据库。

# 根据模型定义创建所有缺失的表
ModelBase.metadata.create_all(bind=db_engine)

# 若需清空表结构(谨慎使用)
# ModelBase.metadata.drop_all(bind=db_engine)

基础数据操纵 (CRUD)

插入记录

db_session = SessionFactory()

# 单条插入
new_author = Author(full_name="Alice Smith", contact_email="alice@domain.com")
db_session.add(new_author)

# 批量插入
authors_batch = [
    Author(full_name="Bob Jones", contact_email="bob@domain.com"),
    Author(full_name="Charlie Brown", contact_email="charlie@domain.com")
]
db_session.add_all(authors_batch)
db_session.commit()

检索记录

# 提取全量数据
all_authors = db_session.query(Author).all()

# 提取单条首个匹配记录
first_author = db_session.query(Author).first()

# 依据主键精确查找
target_author = db_session.get(Author, 1)

更新与删除

# 状态修改
target_author = db_session.get(Author, 1)
if target_author:
    target_author.full_name = "Alice Updated"
    db_session.commit()

# 批量条件更新
db_session.query(Author).filter(Author.full_name.like("Bob%")).update(
    {"contact_email": "updated_bob@domain.com"}, synchronize_session="fetch"
)
db_session.commit()

# 物理删除
db_session.delete(target_author)
db_session.commit()

# 批量条件删除
db_session.query(Author).filter(Author.full_name == "Charlie Brown").delete()
db_session.commit()

高级查询构建

条件过滤与排序分页

from sqlalchemy import or_

# 复合条件与模糊匹配
filtered_authors = db_session.query(Author).filter(
    or_(Author.full_name == "Alice Updated", Author.contact_email.like("%@domain.com"))
).order_by(Author.author_id.desc()).all()

# 分页控制 (偏移与限制)
page_data = db_session.query(Author).offset(10).limit(5).all()

聚合与分组统计

from sqlalchemy import func

# 统计总记录数
total_books = db_session.query(func.count(Book.book_id)).scalar()

# 分组统计每位作者的著作数量
author_stats = db_session.query(
    Author.full_name, func.count(Book.book_id).label('book_count')
).outerjoin(Book, Author.author_id == Book.writer_id).group_by(Author.full_name).all()

多表联合查询

# 显式内连接
joined_results = db_session.query(Author, Book).join(
    Book, Author.author_id == Book.writer_id
).filter(Book.title.ilike("%python%")).all()

关联关系导航

ORM 的核心优势在于通过对象属性直接操作关联数据,而无需手动编写 JOIN 语句。

# 构建包含关联关系的对象图
tech_category = Category(label="Technology")
db_session.add(tech_category)

new_book = Book(
    title="Advanced SQLAlchemy", 
    abstract="Deep dive into ORM.", 
    writer=new_author
)
new_book.categories.append(tech_category)

db_session.add(new_book)
db_session.commit()

# 通过关系属性反向访问
print(f"书籍《{new_book.title}》的作者为: {new_book.writer.full_name}")
print(f"作者 {new_author.full_name} 的所有作品:")
for b in new_author.written_books:
    print(f" - {b.title} (分类: {[c.label for c in b.categories]})")

事务控制机制

合理的事务管理是保证数据一致性的关键,需妥善处理提交与回滚逻辑。

# 基础异常捕获与回滚
try:
    temp_author = Author(full_name="Temp User", contact_email="temp@test.com")
    db_session.add(temp_author)
    db_session.commit()
except Exception as exc:
    db_session.rollback()
    print(f"事务中断,已回滚: {exc}")

# 嵌套事务 (保存点)
with db_session.begin_nested() as nested_txn:
    try:
        nested_author = Author(full_name="Nested User", contact_email="nested@test.com")
        db_session.add(nested_author)
        nested_txn.commit()
    except Exception:
        nested_txn.rollback()

工程化会话管理实践

在 Web 框架或后台任务中,推荐使用上下文管理器来自动化会话的生命周期,防止连接泄漏。

from contextlib import contextmanager

@contextmanager
def transaction_scope():
    """提供一个自动管理提交与回滚的会话上下文"""
    session = SessionFactory()
    try:
        yield session
        session.commit()
    except Exception:
        session.rollback()
        raise
    finally:
        session.close()

# 业务逻辑调用示例
with transaction_scope() as db:
    author = Author(full_name="Context User", contact_email="ctx@domain.com")
    db.add(author)
    # 离开 with 块时自动触发 commit 和 close

相关文章

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...

发表评论

访客

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