migrations/
folder)migrations/
폴더에 저장된 migration files
는 1대1 맵핑된다.커맨드창에서 pip install Flask-Migrate
를 실행한다.
# app.py
from flask_migrate import Migrate
migrate = Migrate(app, db) # Falsk app, SQLAlchemy database와 연결
flask db init
app.py
이 저장된 폴더로 이동한 후 falsk db init
을 해준다.alembic.ini
는 로깅, 파일 이름, 기본 포멧등의 구성을 설정할 수 있는 config scripts 이다.flask db migrate
db.create_all()
을 대체하므로 db.create_all()
은 삭제해준다.todos
를 저장했던 데이터베이스 todoapp
을 삭제하고 다시 생성한 후 flask db migrate
를 한다면 versions
폴더에 다음과 같은 migration이 생성된다.from alembic import op
import sqlalchemy as sa
# revision identifiers, used by Alembic.
revision = '0647a25940c5'
down_revision = None
branch_labels = None
depends_on = None
def upgrade():
# ### commands auto generated by Alembic - please adjust! ###
op.create_table('todos',
sa.Column('id', sa.Integer(), nullable=False),
sa.Column('description', sa.String(), nullable=False),
sa.PrimaryKeyConstraint('id')
)
# ### end Alembic commands ###
def downgrade():
# ### commands auto generated by Alembic - please adjust! ###
op.drop_table('todos')
# ### end Alembic commands ###
Flask db upgrade
and flask db downgrade
todoapp
이라는 데이터베이스를 삭제했다가 다시 생성하면 안에 있던 테이블들은 다 사라진다.
하지만 flask db upgrade
를 한 후 todoapp
내에 있는 테이블을 확인해보면
todoapp=# \dt
릴레이션(relation) 목록
스키마 | 이름 | 종류 | 소유주
--------+-----------------+--------+----------
public | alembic_version | 테이블 | postgres
public | todos | 테이블 | postgres
이와 같이 todos
가 다시 생성된 것을 볼 수 있다. 생성된 내용은 migration에 있는 upgrade()
내용과 같다.
alembic_version
이라는 테이블은 우리의 데이터베이스의 버전을 저장하고 migrations을 관리한다.
또한 여기서 flask db downgrade
를 하게되면 생성되었던 todos
가 다시 사라지는 것을 볼 수 있다. 이것은 migration의 downgrade()
이 실행된 결과이다.
completed
column to test migrationclass Todo(db.model):
__tablename__ = 'todos'
id = db.Column(db.Integer, primary_key=True)
description = db.Column(db.String(), nullable=False)
completed = db.Column(db.Boolean, nullable=False, default=False)
위와 같이 completed라는 컬럼을 추가한 후 커맨드 창에서 flask db migrate
를 실행한다.
INFO [alembic.autogenerate.compare] Detected added column 'todos.completed'
Generating C:\Users\gusfk\Desktop\class-demos\todoapp\migrations\versions\7c53ca560e11_.py ... done
completed라는 새로운 칼럼을 인식하고 그에 따른 migrations인 7c53ca560e11_.py
을 생성한 것을 확인할 수 있다.
# 7c53ca560e11_.py
def upgrade():
# ### commands auto generated by Alembic - please adjust! ###
op.add_column('todos', sa.Column('completed', sa.Boolean(), nullable=False))
# ### end Alembic commands ###
def downgrade():
# ### commands auto generated by Alembic - please adjust! ###
op.drop_column('todos', 'completed')
# ### end Alembic commands ###
내용을 확인한 후 flask db upgrade
를 통해 적용되지 않은 migraions의 가장 최근 버전으로 업그레이드 해준다.
만약 completed
컬럼을 추가하기 전 테이블에 기존 값(id, description)들이 있다면 어떨까?
기존의 테이블에 값들이 존재할 때 flask db upgrade
를 실행하면 다음과 같은 오류가 나타난다.
sqlalchemy.exc.IntegrityError: (psycopg2.errors.NotNullViolation) 오류: "completed" 열에는 null 값 자료가 있습니다
[SQL: 'ALTER TABLE todos ADD COLUMN completed BOOLEAN NOT NULL'] (Background on this error at: http://sqlalche.me/e/gkpj)
우리가 클래스를 작성할 때 completed 컬럼에서 nullable=False
라고 했지만 해당 컬럼이 생기기 전에 작성했던 레코드에서는 null값일 수 밖에 없기 때문이다.
이 문제를 해결하기 위해서는 해당 migration script를 수정해주어야 한다.
# 7c53ca560e11_.py
def upgrade():
# ### commands auto generated by Alembic - please adjust! ###
# op.add_column('todos', sa.Column('completed', sa.Boolean(), nullable=False)) --> nullable=False에서 True로 변경한다.
op.add_column('todos', sa.Column('completed', sa.Boolean(), nullable=True)) #1
op.execute('UPDATE todos SET completed = False WHERE completed IS NULL;') #2
op.alter_column('todos', 'completed', nullable=False) #3
# ### end Alembic commands ###
nullable=True
로 변경하여 기존 값들에도 새로운 스키마가 적용될 수 있도록 한다.op.execute()
로 SQL update문들 이용하여 기존 테이블에 있던 레코드의 completed 칼럼의 값을 null에서 False로 변경한다.completed
컬럼에 새로운 null값들이 올 수 없도록 nullable=False
로 변경해준다.그리고 flask db upgrade
를 한 후 psql을 통해 todos
테이블을 확인해보면
todoapp=# \d todos
"public.todos" 테이블
필드명 | 종류 | Collation | NULL허용 | 초기값
-------------+-------------------+-----------+----------+-----------------------------------
id | integer | | not null | nextval('todos_id_seq'::regclass)
description | character varying | | not null |
completed | boolean | | not null |
todoapp=# select * from todos;
id | description | completed
----+-------------+-----------
1 | todo1 | f
2 | todo2 | f
3 | todo3 | f
completed
컬럼이 잘 추가된 것을 확인할 수 있다.