SqlAlchemy Alembic数据库升级与降级简易教程
前言
通常我们会将我们的代码放入到某个VCS(版本控制系统)中,进行可追溯的版本管理。一个项目除了代码,通常还会有一个数据库,这个数据库可能会随着项目的演进发生变化,甚至需要可以回滚到过去的某个状态,于是一些工具将数据库的版本化也纳入了管理。
Alembic
是Sqlalchemy
的作者实现的一个数据库版本化管理工具,它可以对基于Sqlalchemy的Model与数据库之间的历史关系进行版本化的维护。
开始
Auto Generating Migrations
安装
pip install alembic
初始化
alembic init alembic
执行后会在工程目录下生成以下的目录结构:
.
├── alembic
│ ├── env.py # 配置DataBase.metadata
│ ├── README
│ ├── script.py.mako # 迁移脚本生成模版
│ └── versions # 版本更新(迁移脚本)的内容目录
└── alembic.ini # 配置数据库
数据库模板类
# ./database.py
from flask import Flask
from flask_sqlalchemy import SQLAlchemyapp = Flask(__name__)
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False
app.config['SQLALCHEMY_DATABASE_URI'] = 'mysql+mysqldb://user_name:password@localhost:3306/database_name'
db = SQLAlchemy(app)...emmmm...
配置
- alembic.ini
- 改
sqlalchemy.url = driver://user:pass@localhost/dbname
- 为
mysql+mysqldb://user_name:password@localhost:3306/database_name
- 改
- alembic/env.py
- 改
target_metadata = None
为以下内容:
- 改
import sys
from database import db # 这个路径要改为数据库模板类所在的目录下
# dirname(dirname(abspath(__file__))) -> ./alembic
sys.path.append(os.path.dirname(os.path.abspath(__file__)) + "/../")
from db import dbtarget_metadata = db.metadata
使用
这东西好处就是可以像Git一样升级和降级数据库。
- 这个工具是按照数据库来判断你是否要对数据库进行升级。因此,参照物是数据库表,而变化的是你写的代码。
创建数据库版本[手写]
alembic revision -m "注释"
-m
:注释- 缺点:手写……
跑完之后会在./alembic/versions
生成脚本。
alembic
├── env.py
├── README
├── script.py.mako
└── versions└── 6f406f586bbb_注释.py
脚本内容如下所示:
"""注释Revision ID: 6f406f586bbb
Revises: 8b6caa4ac725
Create Date: 2019-05-14 20:06:39.940623"""
from alembic import op
import sqlalchemy as sa# revision identifiers, used by Alembic.
revision = '6f406f586bbb'
down_revision = '8b6caa4ac725'
branch_labels = None
depends_on = Nonedef upgrade():passdef downgrade():pass
创建一个新表
Operations的文档:Operation Reference
...省略...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')
修改表
"""Add a columnRevision 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 sadef upgrade():op.add_column('account', sa.Column('last_transaction_date', sa.DateTime))def downgrade():op.drop_column('account', 'last_transaction_date')
总而言之,你可以用Alembic来创建和修改表的结构。
创建数据库版本[自动]
alembic revision --autogenerate -m "initdb"
- 自动生成数据库版本
- 说明:当且仅当数据库为空的时候才会自动根据创建的Model创建version版本。
没有数据库表
没有数据库的时候,Alembic会根据模型创建,命令行执行的结果:
INFO [alembic.runtime.migration] Context impl MySQLImpl.
INFO [alembic.runtime.migration] Will assume non-transactional DDL.
INFO [alembic.autogenerate.compare] Detected added table '...'
INFO [alembic.autogenerate.compare] Detected added table '...'
INFO [alembic.autogenerate.compare] Detected added table '...'
INFO [alembic.autogenerate.compare] Detected added table '...'
INFO [alembic.autogenerate.compare] Detected added table '...'Generating /.../project_name/alembic/versions/5e2d2560b497_init_db.py ... done
看看文件内容:
"""init dbRevision ID: 5e2d2560b497
Revises:
Create Date: 2019-05-15 11:11:14.684351"""
from alembic import op
import sqlalchemy as sa# revision identifiers, used by Alembic.
revision = '5e2d2560b497'
down_revision = None
branch_labels = None
depends_on = Nonedef upgrade():# ### commands auto generated by Alembic - please adjust! ###op.create_table('...',sa.Column('id', sa.Integer(), nullable=False),sa.Column('...', sa.String(length=50), nullable=False),sa.Column('...', sa.DateTime(), nullable=True),sa.Column('...', sa.DateTime(), nullable=True),sa.PrimaryKeyConstraint('id'))......op.create_table('...',sa.Column('id', sa.Integer(), nullable=False),sa.Column('...', sa.String(length=50), nullable=False),sa.Column('...', sa.DateTime(), nullable=True),sa.Column('...', sa.DateTime(), nullable=True),sa.PrimaryKeyConstraint('id'))# ### end Alembic commands ###def downgrade():# ### commands auto generated by Alembic - please adjust! ###op.drop_table('...')......op.drop_table('...')# ### end Alembic commands ###
有数据库表
执行后会在./alembic/versions
生成脚本。
alembic
├── env.py
├── README
├── script.py.mako
└── versions└── 8b6caa4ac725_init_db.py
脚本内容如下所示:
"""init dbRevision ID: 8b6caa4ac725
Revises:
Create Date: 2019-05-14 20:02:46.802136"""
from alembic import op
import sqlalchemy as sa# revision identifiers, used by Alembic.
revision = '8b6caa4ac725'
down_revision = None
branch_labels = None
depends_on = Nonedef upgrade():# ### commands auto generated by Alembic - please adjust! ###...这里我省略了,都是对表修改操作...# ### end Alembic commands ###def downgrade():# ### commands auto generated by Alembic - please adjust! ###...这里我省略了,都是对表修改操作...# ### end Alembic commands ###
自动写完了代码,优秀!╮(╯▽╰)╭
有表和没表的区别
Alembic是根据数据库与代码中的Model的定义进行比对,来生成对应的版本号的。(Model可以看下一张的示例)
要是创建了数据库表,而当前的Model并没有任何修改,提交之后upgrade
和downgrade
是为空的。
加入没有创建数据库表,那么就根据Model中的将创建表和删除表的操作分别在upgrade
和downgrade
中编写出来。
示例
Model定义:
class Test(db.Model):id = db.Column(db.Integer, primary_key=True)test1 = db.Column(db.String(50), unique=False, nullable=False)test2 = db.Column(db.String(120), unique=True, nullable=False)create_at = db.Column(db.DateTime, unique=False, nullable=True, default=datetime.utcnow)update_at = db.Column(db.DateTime, unique=False, nullable=True, default=datetime.utcnow)
- 初始化:
alembic init alembic
- 配置(参照前几章)
- 初始化第一个版本
alembic revision --autogenerate -m "init db"
此时数据库表没有创建
INFO [alembic.runtime.migration] Context impl MySQLImpl.
INFO [alembic.runtime.migration] Will assume non-transactional DDL.
INFO [alembic.autogenerate.compare] Detected added table 'test'Generating /.../project_name/alembic/versions/3b63c8e4c302_init_db.py ... done
- 文件
./alembic/versions/3b63c8e4c302_init_db.py
"""init dbRevision ID: 3b63c8e4c302
Revises:
Create Date: 2019-05-15 11:48:45.805342"""
from alembic import op
import sqlalchemy as sa# revision identifiers, used by Alembic.
revision = '3b63c8e4c302'
down_revision = None
branch_labels = None
depends_on = Nonedef upgrade():# ### commands auto generated by Alembic - please adjust! ###op.create_table('test',sa.Column('id', sa.Integer(), nullable=False),sa.Column('test1', sa.String(length=50), nullable=False),sa.Column('test2', sa.String(length=120), nullable=False),sa.Column('create_at', sa.DateTime(), nullable=True),sa.Column('update_at', sa.DateTime(), nullable=True),sa.PrimaryKeyConstraint('id'),sa.UniqueConstraint('test2'))# ### end Alembic commands ###def downgrade():# ### commands auto generated by Alembic - please adjust! ###op.drop_table('test')# ### end Alembic commands ###
- 查看版本:
alembic history
<base> -> 3b63c8e4c302 (head), init db
- 数据库升级:
alembic upgrade 3b63c8
Database changed
mysql> show tables;
+------------------+
| Tables_in_qmasdb |
+------------------+
| alembic_version |
| test |
+------------------+
2 rows in set (0.00 sec)mysql> desc test;
+-----------+--------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+-----------+--------------+------+-----+---------+----------------+
| id | int(11) | NO | PRI | NULL | auto_increment |
| test1 | varchar(50) | NO | | NULL | |
| test2 | varchar(120) | NO | UNI | NULL | |
| create_at | datetime | YES | | NULL | |
| update_at | datetime | YES | | NULL | |
+-----------+--------------+------+-----+---------+----------------+
5 rows in set (0.00 sec)
- 增加一个
hahahahahahahat
字段:
class Test(db.Model):id = db.Column(db.Integer, primary_key=True)test1 = db.Column(db.String(50), unique=False, nullable=False)test2 = db.Column(db.String(120), unique=True, nullable=False)hahahahahahahat = db.Column(db.String(120), unique=True, nullable=False) # ╮(╯▽╰)╭create_at = db.Column(db.DateTime, unique=False, nullable=True, default=datetime.utcnow)update_at = db.Column(db.DateTime, unique=False, nullable=True, default=datetime.utcnow)
- 提交更新:
alembic revision --autogenerate -m "add hahahahahahahat."
INFO [alembic.runtime.migration] Context impl MySQLImpl.
INFO [alembic.runtime.migration] Will assume non-transactional DDL.
INFO [alembic.autogenerate.compare] Detected added column 'test.hahahahahahahat'
INFO [alembic.autogenerate.compare] Detected added unique constraint 'None' on '['hahahahahahahat']'Generating /.../project_name/alembic/versions/290c92587e1a_add_hahahahahahahat.py ... done
- 让我们看看
./alembic/versions/290c92587e1a_add_hahahahahahahat.py
这个文件。
"""add hahahahahahahat.Revision ID: 290c92587e1a
Revises: 3b63c8e4c302
Create Date: 2019-05-15 11:53:28.251516"""
from alembic import op
import sqlalchemy as sa# revision identifiers, used by Alembic.
revision = '290c92587e1a'
down_revision = '3b63c8e4c302'
branch_labels = None
depends_on = Nonedef upgrade():# ### commands auto generated by Alembic - please adjust! ###op.add_column('test', sa.Column('hahahahahahahat', sa.String(length=120), nullable=False))op.create_unique_constraint(None, 'test', ['hahahahahahahat'])# ### end Alembic commands ###def downgrade():# ### commands auto generated by Alembic - please adjust! ###op.drop_constraint(None, 'test', type_='unique')op.drop_column('test', 'hahahahahahahat')# ### end Alembic commands ###
- 看下提交记录:
alembic history
3b63c8e4c302 -> 290c92587e1a (head), add hahahahahahahat.
<base> -> 3b63c8e4c302, init db
- 升级数据库:
alembic upgrade 290c92
INFO [alembic.runtime.migration] Context impl MySQLImpl.
INFO [alembic.runtime.migration] Will assume non-transactional DDL.
INFO [alembic.runtime.migration] Running upgrade 3b63c8e4c302 -> 290c92587e1a, add hahahahahahahat.
- 看看数据库
mysql> desc test;
+-----------------+--------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+-----------------+--------------+------+-----+---------+----------------+
| id | int(11) | NO | PRI | NULL | auto_increment |
| test1 | varchar(50) | NO | | NULL | |
| test2 | varchar(120) | NO | UNI | NULL | |
| create_at | datetime | YES | | NULL | |
| update_at | datetime | YES | | NULL | |
| hahahahahahahat | varchar(120) | NO | UNI | NULL | | # 参数添加进来了╮(╯▽╰)╭
+-----------------+--------------+------+-----+---------+----------------+
6 rows in set (0.00 sec)
使用
Alembic
的好处就是可以控制数据库的版本,增加字段不影响数据。先试试版本更新的。尝试增加
wtfwtfwtfwtfwtf
字段,带unique
关键字……- 提交:
alembic revision --autogenerate -m "add wtfwtfwtfwtf."
- 更新数据库到最新版本:
alembic upgrade head
- 降级到原版本:
alembic downgrade -1
- 提交:
class Test(db.Model):...wtfwtfwtfwtfwtf = db.Column(db.String(120), unique=True, nullable=False)...
- 直接报错了……
TypeError: object of type 'NoneType' has no len()
- 试试添加一个不带
unique
关键字的字段wawawawawawawawa
class Test(db.Model):...wawawawawawawawa = db.Column(db.String(120), nullable=False)...
- 更新:
alembic revision --autogenerate -m "add wawawawawawa."
- 升级:
alembic upgrade head
mysql> select test.id, test.wawawawawawawawa from test;
+----+------------------+
| id | wawawawawawawawa |
+----+------------------+
| 1 | |
| 2 | wawawawawawawawa |
+----+------------------+
2 rows in set (0.00 sec)
- 降级一波:
alembic downgrade -1
INFO [alembic.runtime.migration] Context impl MySQLImpl.
INFO [alembic.runtime.migration] Will assume non-transactional DDL.
INFO [alembic.runtime.migration] Running downgrade ad3ed6b3652c -> de464ecfaa3c, add wawawawawawa.# 数据库
mysql> select test.id, test.wawawawawawawawa from test;
ERROR 1054 (42S22): Unknown column 'test.wawawawawawawawa' in 'field list'
- 重新升级回去:
alembic upgrade head
mysql> select test.id, test.wawawawawawawawa from test;
+----+------------------+
| id | wawawawawawawawa |
+----+------------------+
| 1 | |
| 2 | |
+----+------------------+
2 rows in set (0.00 sec)
- Alembic升级不会影响数据,但是降级一定丢数据。
- 文档中写了一句话:We review and modify these by hand as needed, then proceed normally.安全起见,每次提交之后最好review下自动生成的脚本(
./alembic/versions/....py
)。
其他操作
- 更新数据库:
alembic upgrade 版本号
- 更新到最新版:
alembic upgrade head
- 升两级:
alembic upgrade +2
- 降级到最初版:
alembic downgrade base
- 降级数据库:
alembic downgrade 版本号
- 降两级:
alembic downgrade -2
- 查看当前版本:
alembic current
- 查看历史版本:
alembic history --verbose
alembic history -r版本号:版本号
alembic history -r-1:current
:上一个版本到当前版本alembic history -r8b6caa:6f406f
:8b6caa版本(包含)到6f406f版本(包含)alembic history -r8b6caa:
:8b6caa版本(包含)到当前版本
- 离线更新(生成SQL):
alembic upgrade 版本号 --sql > migration.sql
- 从特定起始版本生成SQL:
alembic upgrade 版本一:版本二 --sql > migration.sql
- 查询当前数据库版本号:
- 进入配置的数据库可以看到一个表
alembic_version
,其中的version_num
关键字就是版本号。
- 进入配置的数据库可以看到一个表
- 清除所有版本:
- 需要将versions下的所有文件删除,并删除数据库表
alembic_version
。
- 需要将versions下的所有文件删除,并删除数据库表
查看支持的模板
alembic list_templates
Available templates:multidb - Rudimentary multi-database configuration.
generic - Generic single-database configuration.
pylons - Configuration that reads from a Pylons project environment.Templates are used via the 'init' command, e.g.:alembic init --template generic ./scripts
附录
- Auto Generating Migrations
- OpenStack数据库版本控制工具简介
- 使用alembic进行数据库版本管理
- 使用alembic进行数据库版本管理
- Python数据库ORM工具sqlalchemy的学习笔记
级联删除
来源于:https://www.v2ex.com/t/507065
from flask import Flask
from flask_sqlalchemy import Model, SQLAlchemy
from sqlalchemy import Column, Integer, DateTime, func, String, ForeignKey
from sqlalchemy.orm import relationship, backrefclass BaseModel(Model):id = Column(Integer, primary_key=True)created_at = Column(DateTime(True), default=func.now(), nullable=False)updated_at = Column(DateTime(True), default=func.now(), onupdate=func.now(), nullable=False)@classmethoddef create(cls, **kw):session = db.sessionif 'id' in kw:obj = session.query(cls).get(kw['id'])if obj:return objobj = cls(**kw)session.add(obj)session.commit()return objdef to_dict(self):columns = self.__table__.columns.keys()return {key: getattr(self, key) for key in columns}app = Flask(__name__)
app.config['SQLALCHEMY_DATABASE_URI'] = 'mysql+mysqldb://root:iqiyi123@localhost:3306/test'db = SQLAlchemy(app, model_class=BaseModel)######################################################################################################class Parent(db.Model):__tablename__ = 'parent'id = Column(Integer, primary_key=True)name = Column(String(20))class Child(db.Model):__tablename__ = 'child'id = Column(Integer, primary_key=True)name = Column(String(20))# 无法删除parent_id = Column(Integer, ForeignKey('parent.id'))parent = relationship("Parent")# 父级,子级一起删除# parent_id = Column(Integer, ForeignKey('parent.id', ondelete="CASCADE"))# parent = relationship("Parent", backref=backref("child", passive_deletes=True))# 父级删除,子级不删除,外键更新为null# parent_id = Column(Integer, ForeignKey('parent.id', ondelete="CASCADE"))# parent = relationship("Parent", backref=backref("child"))db.create_all()
Parent.create(name='ZhangTian')
Parent.create(name='LiTian')
Child.create(name='ZhangDi', parent_id=1)
Child.create(name='LiDi', parent_id=2)parent = db.session.query(Parent).first()
db.session.delete(parent)
db.session.commit()
SqlAlchemy Alembic数据库升级与降级简易教程相关推荐
- Android数据库升级、降级、创建(onCreate() onUpgrade() onDowngrade())的注意点
以下内容可以作为面试官在面试的时候的问题,感觉比较好,是比较常用的知识点,可以用来考察基础是否扎实. 也可以程序猿学习.开发中的注意点.因为稍微不注意,就有可能导致数据库不能用. DBAdapter. ...
- 用 Flask 来写个轻博客 (8) — (M)VC_Alembic 管理数据库结构的升级和降级
Blog 项目源码:https://github.com/JmilkFan/JmilkFan-s-Blog 目录 目录 前文列表 扩展阅读 Alembic 查看指令 manager db 的可用选项 ...
- android数据库降级_Android SQLite (二.数据库创建,升级及降级)
上篇文章简介和常用语法介绍了SQLite数据库的基本信息和一些常用的语法操作,本篇文章主要介绍Android开发过程中SQLite数据库的创建使用和常见问题处理. 一.SQLiteOpenHelper ...
- oracle降版本导出,Oracle 数据库 升级/降级 兼容性矩阵
引用自: Doc ID 1577660.1 升级到19c的升级兼容性矩阵 能够直接升级到Oracle Database 19c的数据库最小版本https://www.cndba.cn/cndba/da ...
- 域控服务器降级失败,windows2003域控制器升级和降级的图文教程
windows2003域控制器升级和降级 按照微软的说法,一般网络中过的PC数目低于10台,则建议建议采对等网的工作模式,而如果超过10台,则建议采用域的管理模式,因为域可以提供一种集中式的管理,这相 ...
- 【与达梦同行】数据库升级实战教程
1 背景 众所周知,IT行业发展日新月异.IT技术革新也是以迅猛著称. 达梦作为最优秀数据库厂商之一在数据库的研发上也是不遗余力,根据市场,客户,前沿技术稳定的对达梦数据库进行迭代. 达梦的数据库新版 ...
- Alembic数据库迁移工具使用
Flask下数据库迁移 背景 在很多时候,在我们设计好模型之后,又需要进行一定的改变和更新数据操作:最直接的方式就是删除原来的旧表,但我们之前的数据也会删除:究其原因是我们不能精确记住每个修改和对应D ...
- android数据库降级_android——数据库版本升/降级问题
数据库版本升级 在开发android应用程序的时候,一般由于在我们开发的时候我们不知道以后会后什么新功能,也有可能增加业务逻辑(也就是更新),可想而知我们原来的数据库结构可能不适用已更新的应用,那么应 ...
- Ocelot简易教程(一)之Ocelot是什么
Ocelot简易教程(一)之Ocelot是什么 原文:Ocelot简易教程(一)之Ocelot是什么 作者:依乐祝 原文地址:https://www.cnblogs.com/yilezhu/p/955 ...
最新文章
- 海康设备字母编号含义 H5:0,H7:1,KT2:2,G5:3
- 数据分析:JSON格式数据
- Django后台管理
- CentOS 7配置IP的几种方法。
- Amazon Aurora 深度探索
- ceph常用命令-pool相关命令
- [转载]CentOS6nbsp;快速搭建轻量级远程桌面nbsp;Xfcenb
- C++类型转换基本语法
- React Native集成Redux框架讲解与应用
- 计算图层面积(针对于面要素)ArcObject c++
- java面试宝典2019_Java面试宝典2019完整版.doc
- RabbitMQ生产者和消费者Java实现
- Apache Mnemonic成为Apache顶级项目,主要解决大数据性能问题
- angularjs 实例_AngularJS过滤器示例教程
- JAVA 反射 动态获取类,并调用方法
- 腾讯云服务器安装AMH控制面板
- Android Studio 写个单元测试用例,就是这么方便
- 解决问题就像剥洋葱,解决的关键是基础知识
- 计算机软件著作权评估
- win10电脑连接蓝牙请检查PIN并重新连接
热门文章
- 很认真的聊一聊程序员的自我修养(转自博客园)
- Android项目练手
- ARKit-带你走进全新的世界(三:追踪/距离感应/AR尺子)
- 关于离职证明和竞业条款
- Translate Shell使用
- 【上海科技大学】考研初试复试资料分享
- MyEclipse下SVN的配置
- C++左值与右值の深思——万能引用与完美转发
- 创建电子文件封装(EEP)包的功能代码
- HTML大学生动漫网页设计作业源码 ~ 火影忍者动漫7页面带特效带轮播(HTML+CSS+JavaScript)...