flask_migrate实现ORM映射数据库管理

flask_migrate实现ORM映射数据库管理

在使用SQLAlcjemy的ORM时,想必大家都遇到过数据库模型更新后无法直接修改至数据库上,必须要将数据库删掉然后才可以将新的表结构映射到数据库中,但是酱汁是有点坑啊,如果业务已经上线运行了一段时间了,这个时间删库删表,简直吊炸天啊。于是乎我们怎么解决这类问题呢?那就看看flask_migrate的功能吧。

flask_migrate简单来说就相当于把orm和数据库的关系映射提取了出来,通过shell的方式操作orm控制数据库。

常用的三个参数如下

init 创建一个新的迁移存储库
migrate 生成一个新的修订版本
upgrade 升级到最新的修订版本

下面我们来看下使用

首先贴一份全代码

#coding:utf-8
from  flask_script import  Manager
#导入flask_script模块,由于我们是在命令行下控制数据库的映射关系建立,所以嘛要依赖这个插件

from flask_sqlalchemy import SQLAlchemy
#这个ORM,必须导入咯

from flask import Flask

from flask_migrate import Migrate,MigrateCommand
#导入flask_migrate中的Migrate模块(用于初始化app)和MigrateCommand命令模块(用于提供命令行命令)

import os

basedir = os.path.abspath(os.path.dirname(__file__))
app = Flask(__name__)
app.config['SECRET_KEY'] = 'zheshiyigekey'
app.config['SQLALCHEMY_DATABASE_URI'] ='sqlite:///' + os.path.join(basedir, 'data2.sqlite')
app.config['SQLALCHEMY_COMMIT_ON_TEARDOWN'] = True
app.config['SQLALCHEMY_TRACK_MODIFICATIONS']=True

db = SQLAlchemy(app)
manager = Manager(app)
#绑定app到Manager上,实现manager.run启动

migrate = Migrate(app,db)
#绑定app和db到migrate上,实现ORM通过migrate管理

manager.add_command('db',MigrateCommand)
#给manager添加MigrateCommand相关的控制命令,父命令为db
#例如python sqldb.py db init

class Article(db.Model):
    __tablename__='article'
    id = db.Column(db.Integer,primary_key=True,autoincrement=True)
    title = db.Column(db.String(64),nullable=False)
    content = db.Column(db.String(64),nullable=False)
    # content2 = db.Column(db.String(64))
    # 注释掉一行,模拟稍后的数据表结构修改

    def __repr__(self):
        return "<%s,%s,%s>"%(self.id,self.title,self.content)

@app.route('/inster/')
def inster():
    #inster
    article1 = Article(title='aaa',content='bbb')
    article2 = Article(title='ccc', content='ddd')
    db.session.add(article1)
    db.session.add(article2)
    db.session.commit()
    return 'inster'

@app.route('/select/')
def select():
    #select
    # result = Article.query.filter(Article.title == 'aaa').all()  #查询所有符合条件的数据
    result = Article.query.all()
    # result = Article.query.filter(Article.title == 'aaa').first()  #查询符合条件的第一条数据,如果没有数据则返回None
    # for i in result:
    #     print i.id
    #     print i.title
    #     print i.content

    print result
    return 'select'

@app.route('/update/')
def update():
    #update
    article1 = Article.query.filter(Article.title == 'aaa').first()
    print article1
    print article1.title
    article1.title = 'new title'
    db.session.commit()
    return "update"

@app.route('/delete/')
def delete():
    #delete
    article1 = Article.query.filter(Article.title == 'aaa').first()
    db.session.delete(article1)
    db.session.commit()
    return "delete"

@app.route('/')
def hello_world():
    return 'Hello World!'

if __name__ == '__main__':
    # app.run(host='0.0.0.0', port=8000, debug=True)
    manager.run()

拿到代码后需要先创建下数据库表

创建新的迁移存储库

(venv) [root@9aa3b82f4987 my_test]# python sqldb.py db init
  Creating directory /data/my_test/migrations ... done
  Creating directory /data/my_test/migrations/versions ... done
  Generating /data/my_test/migrations/README ... done
  Generating /data/my_test/migrations/alembic.ini ... done
  Generating /data/my_test/migrations/env.py ... done
  Generating /data/my_test/migrations/script.py.mako ... done
  Generating /data/my_test/migrations/env.pyc ... done
  Please edit configuration/connection/logging settings in '/data/my_test/migrations/alembic.ini' before proceeding.

#执行后会生成一个migrations的目录,我们可以通过tree看到里面的结构
(venv) [root@9aa3b82f4987 my_test]# tree migrations/
migrations/
├── alembic.ini
├── env.py
├── env.pyc
├── README
├── script.py.mako
└── versions

1 directory, 5 files

生成一个新的修订版本

(venv) [root@9aa3b82f4987 my_test]# python sqldb.py db migrate
INFO  [alembic.runtime.migration] Context impl SQLiteImpl.
INFO  [alembic.runtime.migration] Will assume non-transactional DDL.
INFO  [alembic.autogenerate.compare] Detected added table 'article'
  Generating /data/my_test/migrations/versions/33a330224a8f_.py ... done

(venv) [root@9aa3b82f4987 my_test]# tree migrations/
migrations/
├── alembic.ini
├── env.py
├── env.pyc
├── README
├── script.py.mako
└── versions
    ├── 33a330224a8f_.py
    └── 33a330224a8f_.pyc

1 directory, 7 files

创建到数据库(由于这里使用的是sqlit所以没法直观的看到创建好的表结构,大家可以通过连接mysql看效果)

(venv) [root@9aa3b82f4987 my_test]# python sqldb.py db upgrade
INFO  [alembic.runtime.migration] Context impl SQLiteImpl.
INFO  [alembic.runtime.migration] Will assume non-transactional DDL.
INFO  [alembic.runtime.migration] Running upgrade  -> 33a330224a8f, empty message

创建完成后就可以启动服务了

启动sqldb脚本

python sqldb.py runserver -p 8000 -h 0.0.0.0
 * Running on http://0.0.0.0:8000/ (Press CTRL+C to quit)
#首次访问url http://x.x.x.x:8000/select/ 可以看到控制台的输出是个空列表
[]

#访问下inster页面 http://x.x.x.x:8000/inster/

#再次访问select页面  http://x.x.x.x:8000/select/ 可以看到控制台输出了数据库的查询结果
[<1,aaa,bbb>, <2,ccc,ddd>]

以上的测试表示当前的数据库已经可以使用了

尝试修改表结构

修改表结构

class Article(db.Model):
    __tablename__='article'
    id = db.Column(db.Integer,primary_key=True,autoincrement=True)
    title = db.Column(db.String(64),nullable=False)
    content = db.Column(db.String(64),nullable=False)
    content2 = db.Column(db.String(64))   #将代码中注释的这行取消注释
    def __repr__(self):
        return "<%s,%s,%s>"%(self.id,self.title,self.content)

重启程序python sqldb.py runserver -p 8000 -h 0.0.0.0
访问查询页面

OperationalError: (sqlite3.OperationalError) no such column: article.content2
[SQL: u'SELECT article.id AS article_id, article.title AS article_title,
article.content AS article_content, article.content2 AS article_content2
\nFROM article'] (Background on this error at: http://sqlalche.me/e/e3q8)

访问后提示异常,我们看下异常点,提示我们没有content2列,那是因为我们改了映射关系,
但是数据库中没有更新表结构,所以无法查询成功

通过migrate将新修改的表结构更新到数据库

(venv) [root@9aa3b82f4987 my_test]# python sqldb.py db migrate
INFO  [alembic.runtime.migration] Context impl SQLiteImpl.
INFO  [alembic.runtime.migration] Will assume non-transactional DDL.
INFO  [alembic.autogenerate.compare] Detected added column 'article.content2'
  Generating /data/my_test/migrations/versions/218f86ca1785_.py ... done

(venv) [root@9aa3b82f4987 my_test]# tree migrations/
migrations/
├── alembic.ini
├── env.py
├── env.pyc
├── README
├── script.py.mako
└── versions
    ├── 218f86ca1785_.py
    ├── 218f86ca1785_.pyc
    ├── 33a330224a8f_.py
    └── 33a330224a8f_.pyc

1 directory, 9 files

(venv) [root@9aa3b82f4987 my_test]# python sqldb.py db upgrade
INFO  [alembic.runtime.migration] Context impl SQLiteImpl.
INFO  [alembic.runtime.migration] Will assume non-transactional DDL.
INFO  [alembic.runtime.migration] Running upgrade 33a330224a8f -> 218f86ca1785, empty message

再次启动程序

重启程序python sqldb.py runserver -p 8000 -h 0.0.0.0
访问查询页面http://x.x.x.x:8000/select/
终端返回正常的查询结果[<1,aaa,bbb>, <2,ccc,ddd>]

你可能感兴趣的:(flask_migrate实现ORM映射数据库管理)