利用alembic进行sqlalchemy ORM数据库模型版本管理

说实在的真不知道该怎么起这篇文章的标题。需求是这样的,数据库的设计与定义采用sqlalchemy ORM的方式进行定义,那么如果对需要对数据库结构进行修改呢,谁家的数据库设计也不会保证一步到位啊,这种模式下是不是我要先修改ORM定义,再用一个SQL修改数据库结构,因为当我的数据库已经在运行之后,总不能再Base.metadata.create_all()了吧。alembic就是为了解决这种问题而存在的,其作者正是sqlalchemy作者本人,以前有个sqlalchemy-mirgration的项目是完成这个功能的,但是那个项目已经不更新了,所以sqlalchemy官方推荐用alembic做迁移和版本管理。

安装


安装十分简单,因为是python官方源里的包:

pip install alembic

第一次定义数据结果构


假设我们定义一个联系人通讯录的数据库,包括两个表,文件名为models.py:

#!/usr/bin/env python
# -*- coding:utf-8 -*-
import sqlalchemy as sa

from sqlalchemy.ext.declarative import declarative_base

Base = declarative_base()

class Person(Base):
    __tablename__ = 'person'

    person_id = sa.Column(sa.Integer, primary_key=True)

    nickname = sa.Column(sa.String(64), nullable=False)
    password = sa.Column(sa.String(16))
    gender = sa.Column(sa.Integer)
    birthday = sa.Column(sa.Date)

class Telephone(Base):
    __tablename__ = 'telephone'

    tel_id = sa.Column(sa.Integer, primary_key =True)
    person_id = sa.Column(sa.Integer, sa.ForeignKey('person.person_id'))
    telphone_no = sa.Column(sa.String(64))

if __name__ == "__main__":

    engine = sa.create_engine("sqlite:///test.db")
    Base.metadata.create_all(engine)

上述程序在运行完主程序部分后会在test.db的一个sqlite数据库中自动创建两个表。

修改数据结构


当我们用了一段时间之后,又有了新的需求,我需要存储用户的真实姓名以及身份证号、电子邮箱(每个人的电子邮箱还不只一个),那么我们首先要初始化alembic环境。

初始化alembic环境

在models.py存储的路径下运行:

alembic init alembic

后一个alembic是个路径名称,是指版本管理的相关脚本存储的路径。运行完上述命令后,会在models.py存储的路径下生成一个alembic.ini的文件,和一个alembic的目录,里面有一些文件。首先修改一下alembic.ini文件,找到其中的sqlalchemy.url定义行,将其定义为你的数据库定义串:

sqlalchemy.url = sqlite:///test.db

修改alembic/env.py文件,找到target_metadata那一句前面添加如下语句,并修改target_metadata定义:

import os.path
import sys
sys.path.append(os.path.realpath('.'))
import models

target_metadata = models.Base.metadata

修改数据结构定义

在person表中添加两列:

realname = sa.Column(String(64))
idcard = sa.Column(String(20))

升级数据库

在models.py所在目录下运行:

alembic revision --autogenerate -m 'added two columns to person table'

会得到如下消息:

INFO  [alembic.runtime.migration] Context impl SQLiteImpl.
INFO  [alembic.runtime.migration] Will assume non-transactional DDL.
INFO  [alembic.autogenerate.compare] Detected added column 'person.idcard'
INFO  [alembic.autogenerate.compare] Detected added column 'person.realname'
  Generating /home/lxq/python/test_alembic/alembic/versions/4624827ff13_added_two_
  columns_for_person_table.py ... done

在alembic/versions目录下会生成一个文件4624827ff13_added_two_columns_for_person_table.py,内容大体能看懂,即是版本升级和降级需要的操作:

"""added two columns for person table

Revision ID: 4624827ff13
Revises: 
Create Date: 2015-12-04 23:39:27.369323

"""

# revision identifiers, used by Alembic.
revision = '4624827ff13'
down_revision = None
branch_labels = None
depends_on = None

from alembic import op
import sqlalchemy as sa


def upgrade():
    ### commands auto generated by Alembic - please adjust! ###
    op.add_column('person', sa.Column('idcard', sa.String(length=24), nullable=True))
    op.add_column('person', sa.Column('realname', sa.String(length=64), nullable=True))
    ### end Alembic commands ###


def downgrade():
    ### commands auto generated by Alembic - please adjust! ###
    op.drop_column('person', 'realname')
    op.drop_column('person', 'idcard')

因为这个关于upgrade和downgrade的语句是自动生成的,因此要检查一下里面的内容,是不是你需要的东西,如果不是的话可能需要一些修改,最常见的是如果你只是想修改某一列的名字,它是检测不出来的,它会将你以前的列删除再添加一个新的列。

然后在models.py所在目录下运行:

alembic upgrade head

head是指的版本号,针对哪个版本升级数据库,也可以写上述那个versions目录下生成的那个版本号4624XXXX,如果没有重复的话可以只写前几位。head目前是指向最新的版本。我们用sqlite3 test.db打开这个数据库看一下,可以发现其表格结构已经变了,idcard和realname两个字段已经加进去了,并且还出现了一个存储当前版本号的表alembic_version。
利用alembic进行sqlalchemy ORM数据库模型版本管理_第1张图片

你可以试一下,在上面的基础上再添加一个电子邮件的表格或是downgrade:

alembic downgrade 462-1

-1是指的462XXX的前一个版本,但注意的是sqlite不支持drop column,如果要试验,可以采用postgresql或是别的数据库进行试验。更详细的使用请参照官方的文档: http://alembic.readthedocs.org/en/latest/ 。

你可能感兴趣的:(Python)