Alembic 以 SQLAlchemy 为底层引擎,为关系数据库提供变更管理脚本的创建、管理和执行功能。本教程完整介绍这项工具的原理与用法。
开始前,请确认已经安装 Alembic。安装章节介绍了在本地虚拟环境中安装的常见方式。如该章节所示,通常应将 Alembic 安装到与目标项目相同的模块路径/Python 路径中,一般使用 Python 虚拟环境。这样运行 alembic 命令时,由它调用的 Python 脚本——项目中的 env.py——就能访问应用的模型。这并非严格要求,但通常是更合适的选择。
下文假定 alembic 命令行工具位于本地 PATH 中,并且被调用时能够访问与目标项目相同的 Python 模块环境。
迁移环境
使用 Alembic 的第一步是创建迁移环境(Migration Environment)。它是专属于某个应用的一组脚本目录。迁移环境只创建一次,随后与应用源代码一起维护。通过 Alembic 的 init 命令创建后,还可以按应用的具体需要定制。
包括一些已生成迁移脚本在内,环境结构如下:
yourproject/
alembic.ini
pyproject.toml
alembic/
env.py
README
script.py.mako
versions/
3512b954651e_add_account.py
2b1ae634e5cd_add_order_id.py
3adcc9a56557_rename_username_field.py
该目录包含以下目录和文件:
alembic.ini:Alembic 主配置文件,所有模板都会生成它。后面的编辑 .ini 文件一节会详细说明。pyproject.toml:多数现代 Python 项目都有此文件。Alembic 也可以选择在其中存放与项目有关的配置。使用方法见使用 pyproject.toml 配置。yourproject:应用源代码的根目录,或者根目录中的某个目录。alembic:位于应用源代码树内,是迁移环境所在目录。它可以使用任意名称;使用多个数据库的项目甚至可以有多个这样的目录。env.py:每次调用alembic迁移工具时运行的 Python 脚本。它至少包含配置并生成 SQLAlchemy 引擎、从引擎获取连接和事务,以及使用该连接作为数据库连接来源来调用迁移引擎的指令。将
env.py作为生成环境的一部分,是为了让迁移执行方式能够完全定制。连接的具体方式以及调用迁移环境的细节都在这里。可以修改脚本,使其操作多个引擎、向迁移环境传入自定义参数,或加载并提供应用特定的库和模型。Alembic 提供一组初始化模板,为不同用例提供不同形式的
env.py。README:各种环境模板中都带有该文件,应该包含一些说明性内容。script.py.mako:用于生成新迁移脚本的 Mako 模板文件。其内容会用于生成versions/内的新文件。模板可以编程定制,从而控制每个迁移文件的结构,例如统一导入哪些模块,或改变upgrade()、downgrade()函数结构。举例说,multidb环境可以按upgrade_engine1()、upgrade_engine2()这样的命名方式生成多个函数。versions/:存放各个版本脚本。用过其他迁移工具的读者可能会发现,这些文件不用递增整数编号,而采用部分 GUID。Alembic 依据脚本内部指令确定版本脚本顺序。理论上可以将一个版本文件“插入”其他文件之间,以合并不同分支的迁移序列,不过必须谨慎地手工操作。
创建环境
了解环境的基本概念后,可以使用 alembic init 创建它。以下命令用 generic 模板创建环境:
$ cd /path/to/yourproject
$ source /path/to/yourproject/.venv/bin/activate # assuming a local virtualenv
$ alembic init alembic
上面的 init 命令生成名为 alembic 的迁移目录:
Creating directory /path/to/yourproject/alembic...done
Creating directory /path/to/yourproject/alembic/versions...done
Generating /path/to/yourproject/alembic.ini...done
Generating /path/to/yourproject/alembic/env.py...done
Generating /path/to/yourproject/alembic/README...done
Generating /path/to/yourproject/alembic/script.py.mako...done
Please edit configuration/connection/logging settings in
'/path/to/yourproject/alembic.ini' before proceeding.
该布局由名为 generic 的布局模板生成。Alembic 还包括其他环境模板,可用 list_templates 命令列出:
$ alembic list_templates
Available templates:
generic - Generic single-database configuration.
pyproject - pep-621 compliant configuration that includes pyproject.toml
async - Generic single-database configuration with an async dbapi.
multidb - Rudimentary multi-database configuration.
Templates are used via the 'init' command, e.g.:
alembic init --template generic ./scripts
编辑 .ini 文件
Alembic 在当前目录中放置了 alembic.ini 文件。运行其他命令时,它会在当前目录查找此文件。要指定其他位置,可以使用 --config 选项,或设置 ALEMBIC_CONFIG 环境变量。
generic 创建的一体化 .ini 文件如下:
# A generic, single database configuration.
[alembic]
# path to migration scripts.
# this is typically a path given in POSIX (e.g. forward slashes)
# format, relative to the token %(here)s which refers to the location of this
# ini file
script_location = %(here)s/alembic
# template used to generate migration file names; The default value is %%(rev)s_%%(slug)s
# Uncomment the line below if you want the files to be prepended with date and time
# file_template = %%(year)d_%%(month).2d_%%(day).2d_%%(hour).2d%%(minute).2d-%%(rev)s_%%(slug)s
# Or organize into date-based subdirectories (requires recursive_version_locations = true)
# file_template = %%(year)d/%%(month).2d/%%(day).2d_%%(hour).2d%%(minute).2d_%%(second).2d_%%(rev)s_%%(slug)s
# sys.path path, will be prepended to sys.path if present.
# defaults to the current working directory.
prepend_sys_path = .
# timezone to use when rendering the date within the migration file
# as well as the filename.
# If specified, requires the python>=3.9 or backports.zoneinfo library and tzdata library.
# Any required deps can installed by adding `alembic[tz]` to the pip requirements
# string value is passed to ZoneInfo()
# leave blank for localtime
# timezone =
# max length of characters to apply to the
# "slug" field
# truncate_slug_length = 40
# set to 'true' to run the environment during
# the 'revision' command, regardless of autogenerate
# revision_environment = false
# set to 'true' to allow .pyc and .pyo files without
# a source .py file to be detected as revisions in the
# versions/ directory
# sourceless = false
# version location specification; This defaults
# to <script_location>/versions. When using multiple version
# directories, initial revisions must be specified with --version-path.
# the special token `%(here)s` is available which indicates the absolute path
# to this configuration file.
#
# The path separator used here should be the separator specified by "version_path_separator" below.
# version_locations = %(here)s/bar:%(here)s/bat:%(here)s/alembic/versions
# path_separator (New in Alembic 1.16.0, supersedes version_path_separator);
# This indicates what character is used to
# split lists of file paths, including version_locations and prepend_sys_path
# within configparser files such as alembic.ini.
#
# The default rendered in new alembic.ini files is "os", which uses os.pathsep
# to provide os-dependent path splitting.
#
# Note that in order to support legacy alembic.ini files, this default does NOT
# take place if path_separator is not present in alembic.ini. If this
# option is omitted entirely, fallback logic is as follows:
#
# 1. Parsing of the version_locations option falls back to using the legacy
# "version_path_separator" key, which if absent then falls back to the legacy
# behavior of splitting on spaces and/or commas.
# 2. Parsing of the prepend_sys_path option falls back to the legacy
# behavior of splitting on spaces, commas, or colons.
#
# Valid values for path_separator are:
#
# path_separator = :
# path_separator = ;
# path_separator = space
# path_separator = newline
#
# Use os.pathsep. Default configuration used for new projects.
path_separator = os
# set to 'true' to search source files recursively
# in each "version_locations" directory
# new in Alembic version 1.10
# recursive_version_locations = false
# the output encoding used when revision files
# are written from script.py.mako
# output_encoding = utf-8
# database URL. This is consumed by the user-maintained env.py script only.
# other means of configuring database URLs may be customized within the env.py
# file.
# See notes in "escaping characters in ini files" for guidelines on
# passwords
sqlalchemy.url = driver://user:pass@localhost/dbname
# [post_write_hooks]
# This section defines scripts or Python functions that are run
# on newly generated revision scripts. See the documentation for further
# detail and examples
# format using "black" - use the console_scripts runner,
# against the "black" entrypoint
# hooks = black
# black.type = console_scripts
# black.entrypoint = black
# black.options = -l 79 REVISION_SCRIPT_FILENAME
# lint with attempts to fix using "ruff" - use the module runner, against the "ruff" module
# hooks = ruff
# ruff.type = module
# ruff.module = ruff
# ruff.options = check --fix REVISION_SCRIPT_FILENAME
# Alternatively, use the exec runner to execute a binary found on your PATH
# hooks = ruff
# ruff.type = exec
# ruff.executable = ruff
# ruff.options = check --fix REVISION_SCRIPT_FILENAME
# Logging configuration. This is also consumed by the user-maintained
# env.py script only.
[loggers]
keys = root,sqlalchemy,alembic
[handlers]
keys = console
[formatters]
keys = generic
[logger_root]
level = WARNING
handlers = console
qualname =
[logger_sqlalchemy]
level = WARNING
handlers =
qualname = sqlalchemy.engine
[logger_alembic]
level = INFO
handlers =
qualname = alembic
[handler_console]
class = StreamHandler
args = (sys.stderr,)
level = NOTSET
formatter = generic
[formatter_generic]
format = %(levelname)-5.5s [%(name)s] %(message)s
datefmt = %H:%M:%S
Alembic 用 Python 的 configparser.ConfigParser 库读取 alembic.ini。%(here)s 是一个替换变量,值为 alembic.ini 本身所在位置的绝对路径。借助它,可以生成相对于配置文件位置的正确目录和文件路径。
此文件包含以下配置项:
[alembic]:Alembic 读取此节来确定配置。其核心实现不会直接读取文件中的其他区域,但可由用户定制的env.py读取附加指令(见下方说明)。对于 configparser 配置(不包括pyproject.toml),可以用命令行--name标志自定义alembic节的名称;基本示例见从同一个 .ini 文件运行多个 Alembic 环境。
script_location:Alembic 环境的位置。通常是相对于%(here)s(即配置文件本身位置)的文件系统路径;也可以是相对于当前目录的普通相对路径,或者绝对路径。这是 Alembic 在所有情况下唯一必需的配置键。
alembic init alembic在生成 .ini 文件时会自动将目录名alembic填入这里。也可使用%(here)s/alembic。为了支持将应用打包成 .egg 文件,也可以指定包资源,由
resource_filename()定位文件(0.2.2 新增)。任何包含冒号的非绝对 URI,在这里都会被解释为资源名,而不是普通文件名。file_template:生成新迁移文件的命名方式。如希望在迁移文件名前加上日期和时间,以便按时间排序,可取消示例值的注释。默认值为%%(rev)s_%%(slug)s。可用标记包括:%%(rev)s:修订 ID。%%(slug)s:从修订消息中取得并截断的字符串。%%(epoch)s:依据创建日期产生的纪元时间戳,使用 Python 的datetime.timestamp()方法计算。%%(year)d、%%(month).2d、%%(day).2d、%%(hour).2d、%%(minute).2d、%%(second).2d:创建日期各部分。默认使用datetime.datetime.now(),除非同时使用timezone配置。
file_template还可以包含目录分隔符,从而把迁移文件放在子目录中。使用这种目录路径时,必须将recursive_version_locations设为true。例如:file_template = %%(year)d/%%(month).2d/%%(day).2d_%%(hour).2d%%(minute).2d_%%(second).2d_%%(rev)s_%%(slug)s recursive_version_locations = true它会产生按日期组织的迁移文件,例如
versions/2024/12/26_143022_abc123_add_user_table.py。timezone:可选时区名称(例如UTC、EST5EDT),用于迁移文件注释内及文件名中的时间戳。需要 Python ≥ 3.9,或安装backports.zoneinfo和tzdata。指定后,创建日期不再来自datetime.datetime.now(),而按以下方式生成:datetime.datetime.utcnow().replace( tzinfo=datetime.timezone.utc ).astimezone(ZoneInfo(<timezone>))truncate_slug_length:slug字段可包含的最大字符数,默认为40。sqlalchemy.url:通过 SQLAlchemy 连接数据库的 URL。只有env.py调用它时才使用这个配置。在generic模板中,run_migrations_offline()内的config.get_main_option("sqlalchemy.url")和run_migrations_online()内的engine_from_config(prefix="sqlalchemy.")会引用它。如果 URL 应从其他来源取得,例如环境变量或全局注册表,或者迁移环境需要多个数据库 URL,开发者应按需要修改env.py来获取 URL。revision_environment:设为true时,生成新修订文件以及运行alembic history时,都会无条件运行迁移环境脚本env.py。sourceless:设为true时,versions目录内只有 .pyc 或 .pyo 的文件也会作为版本脚本使用,支持没有源码的版本目录。默认false时只使用 .py 文件。version_locations:可选的修订文件位置列表,允许修订同时位于多个目录。示例见使用多个基线。path_separator:version_locations与prepend_sys_path路径列表的分隔符。仅用于 configparser 配置;pyproject.toml配置不需要它。见使用多个基线。recursive_version_locations:设为true时,在每个version_locations目录中递归搜索修订文件。1.10 新增。output_encoding:Alembic 将script.py.mako写入新迁移文件时使用的编码,默认为utf-8。[loggers]、[handlers]、[formatters]、[logger_*]、[handler_*]、[formatter_*]:这些节属于 Python 标准日志配置,其机制见配置文件格式。与数据库连接一样,这些指令由env.py中的logging.config.fileConfig()调用直接使用,你可以修改该调用。
如果只使用一个数据库和 generic 配置,开始时只需设置 SQLAlchemy URL:
sqlalchemy.url = postgresql://scott:tiger@localhost/test
ini 文件中的字符转义
如前所述,Alembic 用 Python 的 ConfigParser 解析 .ini 文件,并启用其插值功能,以支持 %(here)s 以及创建自定义 Config 对象时通过 Config.config_args 参数设置的其他标记。
因此,字符串中的百分号如果不是插值变量的一部分,就必须重复一次来转义。例如 Python 脚本中的配置值:
my_configuration_value = "some % string"
若要从 .ini 文件解析,应写为:
[alembic]
my_configuration_value = some %% string
示例 alembic.ini 的 file_template 等配置中就体现了这种转义:
# template used to generate migration file names; The default value is %%(rev)s_%%(slug)s
file_template = %%(year)d_%%(month).2d_%%(day).2d_%%(hour).2d%%(minute).2d-%%(rev)s_%%(slug)s
这里真正传给 Alembic 文件生成系统的 file_template 是 %(year)d_%(month).2d_%(day).2d_%(hour).2d%(minute).2d-%(rev)s_%(slug)s。
在 SQLAlchemy URL 中,百分号用于转义 @、百分号自身等具有语法意义的字符。例如密码 "P@ssw%rd":
>>> my_actual_password = "P@ssw%rd"
按 SQLAlchemy 文档所述,URL 中的 @ 和百分号要使用 urllib.parse.quote_plus 转义:
>>> import urllib.parse
>>> sqlalchemy_quoted_password = urllib.parse.quote_plus(my_actual_password)
>>> sqlalchemy_quoted_password
'P%40ssw%25rd'
SQLAlchemy 自身将 URL 转成字符串时,也会使用这种 URL 转义:
>>> from sqlalchemy import URL
>>> URL.create(
... "some_db", username="scott", password=my_actual_password, host="host"
... ).render_as_string(hide_password=False)
'some_db://scott:P%40ssw%25rd@host'
若要将上面转义后的密码字符串放入启用百分号插值的 ConfigParser 文件,还必须把 % 重复一次:
>>> sqlalchemy_quoted_password.replace("%", "%%")
'P%%40ssw%%25rd'
下面是一个完整程序:它依据给定的数据库连接信息组装 URL,显示正确的 configparser 形式,并演示如何用断言检查这些形式:
from sqlalchemy import URL, make_url
database_driver = input("database driver? ")
username = input("username? ")
password = input("password? ")
host = input("host? ")
port = input("port? ")
database = input("database? ")
sqlalchemy_url = URL.create(
drivername=database_driver,
username=username,
password=password,
host=host,
port=int(port),
database=database,
)
stringified_sqlalchemy_url = sqlalchemy_url.render_as_string(
hide_password=False
)
# assert make_url round trip
assert make_url(stringified_sqlalchemy_url) == sqlalchemy_url
print(
f"The correctly escaped string that can be passed "
f"to SQLAlchemy make_url() and create_engine() is:"
f"\n\n {stringified_sqlalchemy_url!r}\n"
)
percent_replaced_url = stringified_sqlalchemy_url.replace("%", "%%")
# assert percent-interpolated plus make_url round trip
assert make_url(percent_replaced_url % {}) == sqlalchemy_url
print(
f"The SQLAlchemy URL that can be placed in a ConfigParser "
f"file such as alembic.ini is:\n\n "
f"sqlalchemy.url = {percent_replaced_url}\n"
)
该程序应能消除将 SQLAlchemy URL 写入 configparser 文件时的歧义:
$ python alembic_pw_script.py
database driver? postgresql+psycopg2
username? scott
password? P@ssw%rd
host? localhost
port? 5432
database? testdb
The correctly escaped string that can be passed to SQLAlchemy make_url() and create_engine() is:
'postgresql+psycopg2://scott:P%40ssw%25rd@localhost:5432/testdb'
The SQLAlchemy URL that can be placed in a ConfigParser file such as alembic.ini is:
sqlalchemy.url = postgresql+psycopg2://scott:P%%40ssw%%25rd@localhost:5432/testdb
使用 pyproject.toml 配置
alembic.ini 中有一部分选项专用于本地环境中 Python 代码的组织和生成。这些选项也可以放到应用的 pyproject.toml 中,以使用符合 PEP 621 的配置。
使用 pyproject.toml 并不排斥同时保留 alembic.ini;后者仍是数据库 URL、连接选项和日志等部署细节的默认配置位置。不过连接和日志配置只由用户管理的 env.py 使用,因此如果这些配置从应用的其他位置取得,迁移环境完全可以不要求 alembic.ini 存在。只有 pyproject.toml、没有 alembic.ini 时,Alembic 仍能成功运行。
要开始使用 pyproject 配置,最直接的方法是选择 pyproject 模板:
alembic init --template pyproject alembic
输出会说明,它正在向现有 pyproject 文件追加指令:
Creating directory /path/to/yourproject/alembic...done
Creating directory /path/to/yourproject/alembic/versions...done
Appending to /path/to/yourproject/pyproject.toml...done
Generating /path/to/yourproject/alembic.ini...done
Generating /path/to/yourproject/alembic/env.py...done
Generating /path/to/yourproject/alembic/README...done
Generating /path/to/yourproject/alembic/script.py.mako...done
Please edit configuration/connection/logging settings in
'/path/to/yourproject/pyproject.toml' and
'/path/to/yourproject/alembic.ini' before proceeding.
如果 pyproject.toml 不存在,Alembic 的模板运行器会创建它;如果存在且尚无 Alembic 指令,就向它追加这些指令。
默认生成的 pyproject.toml 配置节与 alembic.ini 基本相同,但它直接支持值列表,这意味着 prepend_sys_path 和 version_locations 可以写成列表。%(here)s 标记仍表示 pyproject.toml 的绝对路径:
[tool.alembic]
# path to migration scripts
script_location = "%(here)s/alembic"
# template used to generate migration file names; The default value is %%(rev)s_%%(slug)s
# Uncomment the line below if you want the files to be prepended with date and time
# file_template = %%(year)d_%%(month).2d_%%(day).2d_%%(hour).2d%%(minute).2d-%%(rev)s_%%(slug)s
# Or organize into date-based subdirectories (requires recursive_version_locations = true)
# file_template = %%(year)d/%%(month).2d/%%(day).2d_%%(hour).2d%%(minute).2d_%%(second).2d_%%(rev)s_%%(slug)s
# additional paths to be prepended to sys.path. defaults to the current working directory.
prepend_sys_path = [
"."
]
# timezone to use when rendering the date within the migration file
# as well as the filename.
# If specified, requires the python>=3.9 or backports.zoneinfo library and tzdata library.
# Any required deps can installed by adding `alembic[tz]` to the pip requirements
# string value is passed to ZoneInfo()
# leave blank for localtime
# timezone =
# max length of characters to apply to the
# "slug" field
# truncate_slug_length = 40
# set to 'true' to run the environment during
# the 'revision' command, regardless of autogenerate
# revision_environment = false
# set to 'true' to allow .pyc and .pyo files without
# a source .py file to be detected as revisions in the
# versions/ directory
# sourceless = false
# version location specification; This defaults
# to <script_location>/versions. When using multiple version
# directories, initial revisions must be specified with --version-path.
# version_locations = [
# "%(here)s/alembic/versions",
# "%(here)s/foo/bar"
# ]
# set to 'true' to search source files recursively
# in each "version_locations" directory
# new in Alembic version 1.10
# recursive_version_locations = false
# the output encoding used when revision files
# are written from script.py.mako
# output_encoding = "utf-8"
# This section defines scripts or Python functions that are run
# on newly generated revision scripts. See the documentation for further
# detail and examples
# [[tool.alembic.post_write_hooks]]
# format using "black" - use the console_scripts runner,
# against the "black" entrypoint
# name = "black"
# type = "console_scripts"
# entrypoint = "black"
# options = "-l 79 REVISION_SCRIPT_FILENAME"
#
# [[tool.alembic.post_write_hooks]]
# lint with attempts to fix using "ruff" - use the exec runner,
# execute a binary
# name = "ruff"
# type = "exec"
# executable = "%(here)s/.venv/bin/ruff"
# options = "check --fix REVISION_SCRIPT_FILENAME"
该模板的 alembic.ini 会被精简,只保留数据库配置与日志配置:
[alembic]
# database URL. This is consumed by the user-maintained env.py script only.
# other means of configuring database URLs may be customized within the env.py
# file.
sqlalchemy.url = driver://user:pass@localhost/dbname
# Logging configuration. This is also consumed by the user-maintained
# env.py script only.
[loggers]
keys = root,sqlalchemy,alembic
[handlers]
keys = console
[formatters]
keys = generic
[logger_root]
level = WARNING
handlers = console
qualname =
[logger_sqlalchemy]
level = WARNING
handlers =
qualname = sqlalchemy.engine
[logger_alembic]
level = INFO
handlers =
qualname = alembic
[handler_console]
class = StreamHandler
args = (sys.stderr,)
level = NOTSET
formatter = generic
[formatter_generic]
format = %(levelname)-5.5s [%(name)s] %(message)s
datefmt = %H:%M:%S
如果配置 env.py,让它从 alembic.ini 以外的位置获取数据库连接与日志配置,这个文件可以完全省略。
创建迁移脚本
环境就绪后,可以使用 alembic revision 创建新修订:
$ alembic revision -m "create account table"
Generating /path/to/yourproject/alembic/versions/1975ea83b712_create_accoun
t_table.py...done
新文件 1975ea83b712_create_account_table.py 已生成,内容如下:
"""create account table
Revision ID: 1975ea83b712
Revises:
Create Date: 2011-11-08 11:40:27.089406
"""
# revision identifiers, used by Alembic.
revision = '1975ea83b712'
down_revision = None
branch_labels = None
from alembic import op
import sqlalchemy as sa
def upgrade():
pass
def downgrade():
pass
文件包含头部信息、当前修订和“降级”修订的标识符、基本 Alembic 指令的导入,以及空的 upgrade() 和 downgrade() 函数。我们要在两个函数中填入指令,对数据库应用一组更改。通常必须提供 upgrade();只有需要回退到旧修订时才需要 downgrade(),不过提供降级功能通常是个好主意。
还应注意 down_revision 变量。Alembic 用它确定迁移的正确顺序。创建下一个修订时,新文件的 down_revision 会指向当前这个修订:
# revision identifiers, used by Alembic.
revision = 'ae1027a6acf'
down_revision = '1975ea83b712'
Alembic 每次操作 versions/ 目录时都会读取所有文件,并依据 down_revision 标识符之间的连接建立列表;down_revision 为 None 表示第一个文件。理论上,如果环境中有数千个迁移,这可能增加启动延迟,但实践中项目通常应适时裁剪旧迁移(见从头构建最新数据库,了解如何裁剪的同时保留完整构建当前数据库的能力)。
然后向脚本添加指令。假设需要创建一个新的 account 表:
def upgrade():
op.create_table(
'account',
sa.Column('id', sa.Integer, primary_key=True),
sa.Column('name', sa.String(50), nullable=False),
sa.Column('description', sa.Unicode(200)),
)
def downgrade():
op.drop_table('account')
create_table() 与 drop_table() 都是 Alembic 指令。Alembic 用这类指令提供所有基本数据库迁移操作,设计尽可能简洁;多数指令不依赖已有的表元数据。它们利用全局“上下文”获取数据库连接(如果存在连接;迁移也能将 SQL/DDL 指令输出到文件),然后执行命令。与其他内容一样,这个全局上下文在 env.py 中配置。
所有 Alembic 指令的概览见操作参考。
执行第一次迁移
现在来执行迁移。假定数据库完全为空,还没有版本。alembic upgrade 会从当前数据库修订(本例为 None)逐步升级到指定目标修订。可以指定 1975ea83b712,但多数情况下用“最新修订”,即 head,更方便:
$ alembic upgrade head
INFO [alembic.context] Context class PostgresqlContext.
INFO [alembic.context] Will assume transactional DDL.
INFO [alembic.context] Running upgrade None -> 1975ea83b712
成功了!屏幕上的信息来自 alembic.ini 中的日志配置:它将 alembic 日志流输出到控制台,具体是标准错误。
Alembic 首先检查数据库是否有 alembic_version 表,没有就创建它;然后从该表读取当前版本(如果有),计算当前版本到目标版本的路径。本例的目标是 head,也就是 1975ea83b712。随后调用沿途每个文件的 upgrade(),到达目标修订。
执行第二次迁移
再做一次迁移,以便有更多内容可供演示。再次创建修订文件:
$ alembic revision -m "Add a column"
Generating /path/to/yourapp/alembic/versions/ae1027a6acf_add_a_column.py...
done
编辑这个文件,为 account 表添加一列:
"""Add a column
Revision ID: ae1027a6acf
Revises: 1975ea83b712
Create Date: 2011-11-08 12:37:36.714947
"""
# revision identifiers, used by Alembic.
revision = 'ae1027a6acf'
down_revision = '1975ea83b712'
from alembic import op
import sqlalchemy as sa
def upgrade():
op.add_column('account', sa.Column('last_transaction_date', sa.DateTime))
def downgrade():
op.drop_column('account', 'last_transaction_date')
再次升级到 head:
$ alembic upgrade head
INFO [alembic.context] Context class PostgresqlContext.
INFO [alembic.context] Will assume transactional DDL.
INFO [alembic.context] Running upgrade 1975ea83b712 -> ae1027a6acf
数据库中现在已添加 last_transaction_date 列。
部分修订标识符
任何需要显式指定修订编号的地方,都可以使用部分编号。只要能唯一识别该版本,就可以在接受版本编号的任何命令和位置使用:
$ alembic upgrade ae1
这里用 ae1 指代 ae1027a6acf。如果有多个版本以该前缀开头,Alembic 会停止并告知你。
相对迁移标识符
Alembic 也支持相对升级和降级。若从当前修订向前移动两个版本,可以提供十进制形式的 +N:
$ alembic upgrade +2
降级接受负值:
$ alembic downgrade -1
相对标识符也可以以某个指定修订为基准。例如先定位到 ae1027a6acf,再向前升级两步:
$ alembic upgrade ae10+2
获取信息
有了若干修订后,就可以查看状态信息。首先查看当前修订:
$ alembic current
INFO [alembic.context] Context class PostgresqlContext.
INFO [alembic.context] Will assume transactional DDL.
Current revision for postgresql://scott:XXXXX@localhost/test: 1975ea83b712 -> ae1027a6acf (head), Add a column
只有数据库的修订标识符与最新修订匹配时,才显示 head。
还可用 alembic history 查看历史。--verbose 选项会显示每个修订的完整信息;包括 history、current、heads 和 branches 在内的多个命令都接受它:
$ alembic history --verbose
Rev: ae1027a6acf (head)
Parent: 1975ea83b712
Path: /path/to/yourproject/alembic/versions/ae1027a6acf_add_a_column.py
add a column
Revision ID: ae1027a6acf
Revises: 1975ea83b712
Create Date: 2014-11-20 13:02:54.849677
Rev: 1975ea83b712
Parent: <base>
Path: /path/to/yourproject/alembic/versions/1975ea83b712_add_account_table.py
create account table
Revision ID: 1975ea83b712
Revises:
Create Date: 2014-11-20 13:02:46.257104
查看历史范围
使用 alembic history 的 -r 选项,可以查看不同的历史片段。其参数形式是 [start]:[end]。两端都可以是修订编号,或 head、heads、base 等符号,也可用 current 指定当前修订;[start] 支持负的相对范围,[end] 支持正的相对范围:
$ alembic history -r1975ea:ae1027
以下相对范围从三个修订之前开始,直到当前迁移。它会对数据库调用迁移环境,以获得当前迁移:
$ alembic history -r-3:current
查看从 1975 开始到最新修订的所有版本:
$ alembic history -r1975ea:
降级
可以调用 alembic downgrade 回到起点,在 Alembic 中称为 base,以演示如何降级到没有任何迁移的状态:
$ alembic downgrade base
INFO [alembic.context] Context class PostgresqlContext.
INFO [alembic.context] Will assume transactional DDL.
INFO [alembic.context] Running downgrade ae1027a6acf -> 1975ea83b712
INFO [alembic.context] Running downgrade 1975ea83b712 -> None
回到空状态之后,再升级:
$ alembic upgrade head
INFO [alembic.context] Context class PostgresqlContext.
INFO [alembic.context] Will assume transactional DDL.
INFO [alembic.context] Running upgrade None -> 1975ea83b712
INFO [alembic.context] Running upgrade 1975ea83b712 -> ae1027a6acf
下一步
绝大多数 Alembic 环境都会大量使用“自动生成(autogenerate)”功能。请继续阅读下一节自动生成迁移。











暂无评论内容