python 使用 Alembic 进行数据库迁移

2,634 阅读3分钟

一、简介

Alembic是一个轻量的数据库迁移工具,使用 SQLAlchemy 作为底层引擎,为数据库提供变更管理脚本的创建、管理和调用。

二、安装

pip install alembic

三、使用

创建环境,生成一个迁移目录alembic

cd /path/to/yourproject
alembic init alembic

可以使用list_templates 列出环境模板:

alembic list_templates
Available templates:

generic - Generic single-database configuration.
async - Generic single-database configuration with an async dbapi.
multidb - Rudimentary multi-database configuration.
pylons - Configuration that reads from a Pylons project environment. 

创建环境的时候可以指定环境模板:

alembic init --template pylons ./scripts

迁移目录结构如下:

yourproject/
    alembic/
        env.py
        README
        script.py.mako
        versions/
            3512b954651e_add_account.py
            2b1ae634e5cd_add_order_id.py
            3adcc9a56557_rename_username_field.py
  • yourproject - 应用程序源代码的根目录

  • alembic- 迁移环境的主目录

  • env.py- 包含配置和生成 SQLAlchemy 引擎的指令,从该引擎获取连接以及事务,然后调用迁移引擎,使用连接作为数据库连接的来源

  • README - 信息文件

  • script.py.mako- 用于生成新的迁移脚本,用于在versions。可以控制每个迁移文件的结构,包括每个迁移文件中的标准导入,以及对upgrade()downgrade()函数结构的更改。例如,multidb环境允许使用命名方案生成多个函数upgrade_engine1()upgrade_engine2()

  • versions/- 该目录包含各个版本的脚本

配置 .ini 文件:

alembic脚本在调用时查找的文件,基本格式如下:

[alembic]
# 迁移脚本路径
script_location = alembic

# 用于生成新迁移文件的模板
# file_template = %%(rev)s_%%(slug)s

# 添加路径到sys.path 路径之前
prepend_sys_path = .


# 时区名称
# timezone =

# slug 字符最大长度
# truncate_slug_length = 40


# 设置为true时,执行revision命令时,无视自动生成
# revision_environment = false


# 设置true时允许.pyc和.pyo文件作为版本控制,允许无源版本文件夹,
# 当保留默认值 'false' 时,只有 .py 文件被用作版本文件。
# sourceless = false

# 修订文件位置列表,允许修订同时存在于多个目录中
# version_locations = %(here)s/bar:%(here)s/bat:${script_location}/versions

# version path separator; As mentioned above, this is the character used to split
# version_locations. Valid values are:
#
# version_path_separator = :
# version_path_separator = ;
# version_path_separator = space
version_path_separator = os  # default: use os.pathsep

# the output encoding used when revision files
# are written from script.py.mako
# output_encoding = utf-8

# 通过 SQLAlchemy 连接到数据库的 URL
sqlalchemy.url = driver://user:pass@localhost/dbname


# 日志配置
[loggers]
keys = root,sqlalchemy,alembic

[handlers]
keys = console

[formatters]
keys = generic

[logger_root]
level = WARN
handlers = console
qualname =

[logger_sqlalchemy]
level = WARN
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

[loggers],[handlers],[formatters],[logger_*],[handler_*],[formatter_*]- 这些部分都是 Python 标准日志记录配置的一部分。

创建迁移脚本:

环境就绪后,我们可以使用 alembic revision 命令创建新迁移脚本:

$ alembic revision -m "create account table"
Generating /path/to/yourproject/alembic/versions/1975ea83b712_create_accoun
t_table.py...done

将会在versions目录下生成一个新文件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

需要注意的是down_revision变量,None代表第一个文件,当我们创建下一个修订版时,新文件的down_revision标识符将指向上一次的 revision 。

然后我们可以向我们的脚本添加一些指令,假设添加一个新表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')

执行迁移:

alembic upgrade head

head 代表最新的版本,如果我们想要迁移到指定版本,只需指定部分版本号:

alembic upgrade ae1

ae1用来指代修订版ae1027a6acf

还支持相对升级/降级。要从当前移动两个版本,可以提供十进制值“+N”:

alembic upgrade +2 

降级接受负值:

alembic downgrade -1

相对标识符也可以是特定版本。例如,升级到修订版ae1027a6acf加上两个额外的步骤:

alembic upgrade ae10+2

四、自动生成迁移

如果我们不想每次都自己手动修改迁移脚本,可以使用自动生成迁移脚本功能。

首先修改 env.py:

target_metadata = None

改为:

from myapp.mymodel import Base
target_metadata = Base.metadata

如果程序具有多个 Model :

from myapp.mymodel1 import Model1Base
from myapp.mymodel2 import Model2Base
target_metadata = [Model1Base.metadata, Model2Base.metadata]

MetaData在自动生成过程中将按顺序查询集合序列。

创建迁移文件:

alembic revision --autogenerate -m "Added account table"