flask 使用Flask-Migrate遷移數據庫(創建遷移環境、生成遷移腳本、更新數據庫) --


使用Flask-Migrate遷移數據庫

在開發時,以刪除表再重建的方式更新數據庫簡單直接,但明顯的缺陷是會丟掉數據庫中的所有數據。在生產環境下,沒有人想把數據都刪除掉,這時需要使用數據庫遷移工具來完成這個工作。SQLAlchemy的開發者Michael Bayer寫了一個數據庫遷移工作—Alembic來幫助我們實現數據庫的遷移,數據庫遷移工具可以在不破壞數據的情況下更新數據庫表的結構。蒸餾器(Alembic)是煉金術士最重要的工具,要學習SQL煉金術(SQLAlchemy),當然要掌握蒸餾器的使用。

擴展Flask-Migrate繼承了Alembic,提供了一些flask命令來簡化遷移工作,我們將使用它來遷移數據庫。Flask-Migrate及其依賴(主要是Alembic)可以使用pipenv安裝:

 

(Lenovo-ezd1lI9Y) C:\Users\Lenovo>pipenv install flask-migrate

 

 

在程序中,我們實例化Flask-Migrate提供的Migrate類,進行初始化操作:

from flask import Flask, render_template, flash, url_for, redirect
from flask_sqlalchemy import SQLAlchemy
from flask_migrate import Migrate

app = Flask(__name__)
db = SQLAlchemy(app)
migrate = Migrate(app, db)  # 在db對象創建后調用
 

實例化Migrate類時,除了傳入程序app,還需要傳入實例化Flask-SQLAlchemy提供的SQLAlchemy類創建的db對象作為第二個參數。

1、創建遷移環境--flask db init

在開始遷移數據之前,需要先使用下面的命令創建一個遷移環境:

(Lenovo-ezd1lI9Y) D:\flask\FLASK_PRACTICE\DataBase>flask db init
Creating directory D:\flask\FLASK_PRACTICE\DataBase\migrations ... done
Creating directory D:\flask\FLASK_PRACTICE\DataBase\migrations\versions ... done
Generating D:\flask\FLASK_PRACTICE\DataBase\migrations\alembic.ini ... done
Generating D:\flask\FLASK_PRACTICE\DataBase\migrations\env.py ... done
Generating D:\flask\FLASK_PRACTICE\DataBase\migrations\env.pyc ... done
Generating D:\flask\FLASK_PRACTICE\DataBase\migrations\README ... done
Generating D:\flask\FLASK_PRACTICE\DataBase\migrations\script.py.mako ... done
Please edit configuration/connection/logging settings in 'D:\\flask\\FLASK_PRACTICE\\DataBase\\migrations\\alembic.ini' before proceeding.

 

 

Flask-Migrate提供了一個命令集,使用db作為命令集名稱,它提供的命令都以flask db開頭。你可以在命令行中輸入flask—help查看所有可用的命令和說明。

(Lenovo-ezd1lI9Y) D:\flask\FLASK_PRACTICE\DataBase>flask db --help
Usage: flask db [OPTIONS] COMMAND [ARGS]...

  Perform database migrations.

Options:
  --help  Show this message and exit.

Commands:
  branches   Show current branch points
  current    Display the current revision for each database.
  downgrade  Revert to a previous version
  edit       Edit a revision file
  heads      Show current available heads in the script directory
  history    List changeset scripts in chronological order.
  init       Creates a new migration repository.
  merge      Merge two revisions together, creating a new revision file
  migrate    Autogenerate a new revision file (Alias for 'revision...
  revision   Create a new revision file.
  show       Show the revision denoted by the given symbol.
  stamp      'stamp' the revision table with the given revision; don't run...
  upgrade    Upgrade to a later version

 

 

遷移環境只需要創建一次。這會在你的項目根目錄下創建一個migrations文件夾其中包含了自動生成的配置文件和遷移版本文件夾。

 

 

2、生成遷移腳本--flask db migrate -m "add note timestamp"

使用migrate子命令可以自動生成遷移腳本:

(Lenovo-ezd1lI9Y) D:\flask\FLASK_PRACTICE\DataBase>flask db migrate -m "add note timestamp"
INFO  [alembic.runtime.migration] Context impl SQLiteImpl.
INFO  [alembic.runtime.migration] Will assume non-transactional DDL.
INFO  [alembic.autogenerate.compare] Detected removed table u'draft'
INFO  [alembic.autogenerate.compare] Detected removed table u'post'
INFO  [alembic.autogenerate.compare] Detected removed table u'comment'
Generating D:\flask\FLASK_PRACTICE\DataBase\migrations\versions\cdd9d12762fc_add_note_timestamp.py ... done

 

 

這條命令可以簡單理解為在flask里對數據庫(db)進行遷移(migrate)。-m選項用來添加遷移備注信息。從上面的輸出信息我們可以看到,Alembic檢測出了模型變化:表note新加了一個timestamp列,並且相應生成了一個遷移腳本cdd9d12762fc_add_note_timestamp.py:

"""add note timestamp

Revision ID: 7f3dae8cae4d
Revises: 
Create Date: 2019-04-01 21:56:32.469000

"""
from alembic import op
import sqlalchemy as sa


# revision identifiers, used by Alembic.
revision = '7f3dae8cae4d'
down_revision = None
branch_labels = None
depends_on = None


def upgrade():
    # ### commands auto generated by Alembic - please adjust! ###
    op.drop_table('draft')
    op.drop_table('post')
    op.drop_table('comment')
    op.add_column('note', sa.Column('timeStamp', sa.String(length=70), nullable=True))
    op.create_unique_constraint(None, 'note', ['timeStamp'])
    # ### end Alembic commands ###


def downgrade():
    # ### commands auto generated by Alembic - please adjust! ###
    op.drop_constraint(None, 'note', type_='unique')
    op.drop_column('note', 'timeStamp')
    op.create_table('comment',
    sa.Column('id', sa.INTEGER(), nullable=False),
    sa.Column('body', sa.TEXT(), nullable=True),
    sa.Column('post_id', sa.INTEGER(), nullable=True),
    sa.ForeignKeyConstraint(['post_id'], [u'post.id'], ),
    sa.PrimaryKeyConstraint('id')
    )
    op.create_table('post',
    sa.Column('id', sa.INTEGER(), nullable=False),
    sa.Column('title', sa.VARCHAR(length=50), nullable=True),
    sa.Column('body', sa.TEXT(), nullable=True),
    sa.PrimaryKeyConstraint('id')
    )
    op.create_table('draft',
    sa.Column('id', sa.INTEGER(), nullable=False),
    sa.Column('body', sa.TEXT(), nullable=True),
    sa.Column('edit_time', sa.INTEGER(), nullable=True),
    sa.PrimaryKeyConstraint('id')
    )
    # ### end Alembic commands ###
 

從上面的代碼可以看出,遷移腳本主要包含了兩個函數:upgrate()函數用來將改動應用到數據庫,函數中包含了向表中添加timestamp字段的命令,而downgrade()函數用來撤消改動,包含了刪除timestamp字段的命令。

 

就像這兩個函數中的注釋所說的,遷移命令是有Alembic自動生成的,其中可能包含錯誤,所以有必要在生成后檢查一下。

因為每一次遷移都會生成新的遷移腳本,而且Alemic為每一次遷移都生成了修訂版本(revision)ID,所以數據庫可以恢復到修改歷史中的任一點。正因如此,遷移環境中的文件也要納入版本控制。

有些復雜的錯誤無法實現自動遷移,這時可以使用revision命令手動創建遷移腳本。這同樣會生成一個遷移腳本,不過腳本中的upgrade()和downgrade()函數都是空的。你需要使用Alembic提供的Operations對象指令在這兩個函數中實現具體操作,具體可以訪問Alembic官方文檔查看。

3、更新數據庫--flask db upgrade

生成了遷移腳本后,使用upgrade子命令即可更新數據庫:

(Lenovo-ezd1lI9Y) D:\flask\FLASK_PRACTICE\DataBase>flask db upgrade
INFO  [alembic.runtime.migration] Context impl SQLiteImpl.
INFO  [alembic.runtime.migration] Will assume non-transactional DDL.
INFO  [alembic.runtime.migration] Running upgrade  -> 7f3dae8cae4d, add note timestamp
ERROR [root] Error: No support for ALTER of constraints in SQLite dialect

 

如果還沒有創建數據庫和表,這個命令會自動創建,如果已經創建,則會在不損壞數據的前提下執行更新。

從客戶端可以看到note表新增加了一個timestamp字段

 

如果你想回滾遷移,那么可以使用downgrade命令(降級),它會撤銷最后一次遷移在數據庫中的改動,這在開發時非常有用。比如,當執行upgrade命令后發現某些地方出錯了,這時就可以執行flask db downgrade命令進行回滾,刪除對應的遷移腳本,重新生成遷移腳本后再進行更新(upgrade)。


SQLite遷移問題:flask db downgrade 報錯
(Lenovo-ezd1lI9Y) D:\flask\FLASK_PRACTICE\DataBase>flask db downgrade 5e87b4da6187
INFO  [alembic.runtime.migration] Context impl SQLiteImpl.
INFO  [alembic.runtime.migration] Will assume non-transactional DDL.
ERROR [root] Error: Destination 5e87b4da6187 is not a valid downgrade target from current head(s)

 

是因為sqlite不支持alter機制的原因

開發時是否需要遷移?

在生產環境下,當對數據庫結構進行修改后,進行數據庫遷移是必要的。因為你不想損壞任何數據,在生成自動遷移腳本之后,執行更新之前,對遷移腳本進行檢查,甚至是使用備份的數據庫進行遷移測試,都是有必要的。

在開發環境中,你可以按需要選擇是否進行數據庫遷移,對於大多數程序來說,可以在開發時使用虛擬數據生成工具來生成虛擬數據,從而避免手動創建記錄進行測試,這樣每次更改表結構時,可以直接清楚后重新生成,然后生成測試數據,這要比執行一次遷移簡單的多(后續會學到通過一個命令完成所有工作),除非生成虛擬數據耗費的時間過長。

另外,在本地本地開發時通常使用SQLite作為數據庫引擎。SQLite不支持ALTER語句,而這正是遷移工具依賴的工作機制,也就是說,當SQLite數據庫的字段刪除或修改后,我們沒法直接使用遷移工具進行更新,你需要手動添加遷移代碼進行遷移。在開發中,修改和刪除列是很常見的行為,手動操作遷移會花費太多時間。

 

對於SQLite,遷移工具一般使用”move and copy”的工作流(創建新表、遷移數據、刪除舊表)達到類似的效果。

如果希望讓生產環境的部署更搞笑,則應該盡可能讓開發環境和生產環境保持一致。這時應該考慮直接在本地使用MySQL或其他的DBMS,然后設置遷移環境。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM