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>]

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 202,607评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,047评论 2 379
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 149,496评论 0 335
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,405评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,400评论 5 364
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,479评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,883评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,535评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,743评论 1 295
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,544评论 2 319
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,612评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,309评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,881评论 3 306
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,891评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,136评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,783评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,316评论 2 342