使用Alembic维护数据库结构

日常的工作就是在数据库上做增删改查操作。在刀耕火种的年代使用SQL文件保存数据库的维护记录,这样的手动操作,效率低,而且容易出错。所以现在的框架里都有专业的工具来管理数据库的维护记录,避免直接操作数据库。Laravel 框架里有 db migration 工具。

Python里使用 Alembic 维护数据。Alembic是SQLAlchemy团队的作品,熟悉SQLAlchemy,对Alembic的使用就不陌生。把数据库操作生成好python文件,就方便进行版本控制。

那如果已有数据库,但是需要生成model code文件,可以使用sqlacodegen(https://github.com/agronholm/sqlacodegen)

简单的用法:

sqlacodegen postgresql+psycopg2://postgres:server_pwd@127.0.0.1:5432/test_db --outfile=db_model.py

在这里操作使用的数据库是PostgreSQL。

安装好alembic后,先初始化环境:

$ alembic init migrate

migrate可以改成其它名字,数据库维护文件保存在这里。执行完成后,项目的根目录会生成一个alembic.ini文件

修改alembic.ini,更新数据库的连接信息

PostgreSQL:sqlalchemy.url = postgresql://postgres:passwd@127.0.0.1:5432/test
MySQLmysql+pymysql://root:password@127.0.0.1:3306/test

简单的环境初始化工作完成。

开始维护数据库

新建账号表$ alembic revision -m "create account table"

和git相似,这个操作创建一个数据库的维护版本。-m参数是这次版本的备注信息。这个命令执行成功会在migrate/versions目录生成一个py文件,文件名类似于1975ea83b712_create_account_table.py,文件名前面的1975ea83b712是版本号,后面是注释信息。
这个文件里两个空方法,upgrade表示升级时执行,downgrade表示回退版本执行。
版本号不是数字,alembic运行会遍历所有的versions目录里的文件。down_revision为None就是代表数据库维护记录的第一个文件。

在upgrade、downgrade里写好相对的代码后,开始执行

$ alembic upgrade head

这个时候需求变了,产品觉得,还是上个版本好,只好回退一波了

$ alembic downgrade -1

不是,老板还是觉得之前做的那个好,再加回来吧

$ alembic upgrade +1

反复横跳应付自如。

查看当前版本信息

$ alembic current

下面是个简单的例子,上手操作一下。

alembic revision -m "create account table"

开始第一个创建第一个版本。这里的备注我随便写的。实践中最好写一个「信达雅」的备注信息。

在这个版本里我们添加用户表。我们需要用户表自增的uid。这里使用op.execute创建序列对象

op.execute(
    sa.schema.CreateSequence(
        sa.schema.Sequence(
            name='user_id_seq',
            start=1,
            increment=1,
            minvalue=1
        )
    )
)

创建表,设置server_default的值,指定nextval为user_id_seq

op.create_table(
    'user',
    sa.Column(name='uid', type_=sa.BigInteger, server_default=sa.text("nextval('user_id_seq'::regclass)")),
    sa.Column(name='username', type_=sa.String(128), nullable=False,
              server_default=sa.text("''::charactervarying"), comment='用户名'),
    sa.Column(name='reg_time', type_=sa.DateTime, nullable=False,
              server_default=sa.text("'1970-01-0108:00:00'::timestampwithouttimezone"), comment='注册时间'),
    sa.Column(name='status', type_=sa.SmallInteger, nullable=False,
              server_default=sa.text("1"), comment='用户状态;1正常;2禁用'),
)

uid设置主键

op.create_primary_key(constraint_name='user_pk',table_name='user',columns=['uid'])

username设置唯一键

op.create_unique_constraint(constraint_name='u_uk',table_name='user',columns=['username'])

reg_time设置索引

op.create_index(index_name='u_ik',table_name='user',columns=['reg_time'])

表添加备注

op.create_table_comment(table_name='user',comment='用户表')

这些是升级时的执行操作,添加在upgrade里。对应的downgrade方法里添加回退版本操作。

删除表

op.drop_table('user')

删除序列对象

op.execute(
    sa.schema.DropSequence(
        sa.schema.Sequence(
            name='user_id_seq'
        )
    )
)

注意,要先删除引用序列对象的表,然后再删除序列对象。

执行一下改动

$ alembic upgrade head

数据库里就多出来user表。

创建外键。表添加外键限制,可以使用ForeignKey方法来指定关系。

sa.Column(
    sa.ForeignKey(
        column='user.uid',
        name='fp_aq_user_id',
        ondelete='CASCADE',
        onupdate='CASCADE'
    ),
    name='uid',
    type_=sa.BigInteger,
    comment='博文的作者uid'
)