Python 现代开发学习笔记

(十七)从 SQL 操作接到 ORM、会话与迁移

1. 对象映射与会话

目前从 SQL 行中取出字段,再手动建立 Book。对象关系映射(ORM)会把类、属性与表列的对应关系写成模型,让工具生成查询和更新语句。它仍要处理刚才出现的键、关联、连接与重复聚合,不会因为返回了对象就改变数据关系。

以 SQLAlchemy 的会话为例,同一会话可以按主键跟踪同一行对应的对象,记录待写入的新对象或字段变化。flush 把待处理变化送成 SQL,事务随后还可能回滚;commit 才提交事务。数据库连接负责通信与事务,会话还负责对象身份与变化跟踪,这两层职责分开阅读。SQLAlchemy 会话

对象变化送成 SQL,再经过事务提交或回滚

例如连续查出同一编号时,会话可以继续关联同一个对象;若每次都手动从行建立新实例,就得到不同的 Python 对象。理解第四章的对象身份、别名和修改动作后,再看会话保存的状态会更清楚。

2. 模型改变后,已有表也要升级

给 Python 类增加字段,不会自动给已有数据库加一列。迁移记录描述怎样把旧结构改成新结构,同时保留已有数据。先用一个版本号观察最小的升级过程:

# user_version 保存应用自己的结构版本;版本 0 升级时增加 genre 字段。
from contextlib import closing
import sqlite3


def upgrade(connection):
    version = connection.execute("PRAGMA user_version").fetchone()[0]
    if version == 0:
        with connection:
            connection.execute("ALTER TABLE books ADD COLUMN genre TEXT NOT NULL DEFAULT ''")
            connection.execute("PRAGMA user_version = 1")


with closing(sqlite3.connect(":memory:", autocommit=False)) as connection:
    with connection:
        connection.execute("CREATE TABLE books(title TEXT)")
        connection.execute("INSERT INTO books VALUES (?)", ("Python",))
    upgrade(connection)
    upgrade(connection)
    print(connection.execute("PRAGMA user_version").fetchone()[0])
    print(connection.execute("SELECT title, genre FROM books").fetchall())

输出 1 与 [('Python', '')]。第二次检查已经是版本 1,不会再次加列;旧记录继续存在,并取得新增字段的默认值。实际迁移工具把这些变化整理成有顺序的脚本,并记录当前到达哪个版本。Web 章节会结合 ORM 与 Alembic 或框架自己的迁移系统使用。迁移脚本与版本

笔记正文与全部题解 · Ctrl / ⌘ K