IT/Python
SQLAlchemy-Utils 사용시 psycopg2.errors.UndefinedFunction: operator does not exist: character varying = bytea 에러가 발생할 경우 해결 방법
묵회
2021. 11. 18. 13:02
https://blog.linewalks.com/archives/7645
» SQLAlchemy와 cryptography로 민감한 데이터 암호화하기
라인웍스는 방대한 의료데이터속에 숨겨진 무한한 가치를 발견하는 헬스케어 빅데이터 스타트업입니다.
blog.linewalks.com
위 내용을 기반으로 저희 뷰노 서비스에 데이터베이스 암호화를 시도 하였지만 잘 되지 않았다.
문제는 alembic을 통한 database migrate이 제대로 동작하지 않았기 때문이었다.
from alembic import op
import sqlalchemy as sa
revision = '9992ce4adeb1'
down_revision = None
branch_labels = None
depends_on = None
def upgrade():
op.create_table('user',
sa.Column('id', sa.Integer(), nullable=False),
sa.Column('created_at', sa.DateTime(timezone=True), nullable=True),
sa.Column('login_id', sa.String(), nullable=False),
sa.Column('mobile', sqlalchemy_utils.types.encrypted.encrypted_type.EncryptedType(), nullable=False),
sa.Column('name', sqlalchemy_utils.types.encrypted.encrypted_type.EncryptedType(), nullable=False),
sa.Column('hashed_password', sa.String(), nullable=False),
sa.Column('is_superuser', sa.Boolean(), nullable=True),
sa.Column('status', sa.Integer(), nullable=True),
sa.Column('email', sqlalchemy_utils.types.encrypted.encrypted_type.EncryptedType(), nullable=True),
sa.Column('birth_date', sqlalchemy_utils.types.encrypted.encrypted_type.EncryptedType(), nullable=True),
sa.Column('sex', sqlalchemy_utils.types.encrypted.encrypted_type.EncryptedType(), nullable=True),
sa.Column('height', sqlalchemy_utils.types.encrypted.encrypted_type.EncryptedType(), nullable=True),
sa.Column('nation', sqlalchemy_utils.types.encrypted.encrypted_type.EncryptedType(), nullable=True),
sa.Column('race', sqlalchemy_utils.types.encrypted.encrypted_type.EncryptedType(), nullable=True),
sa.PrimaryKeyConstraint('id')
)
op.create_index(op.f('ix_user_email'), 'user', ['email'], unique=False)
op.create_index(op.f('ix_user_id'), 'user', ['id'], unique=False)
op.create_index(op.f('ix_user_login_id'), 'user', ['login_id'], unique=True)
op.create_index(op.f('ix_user_mobile'), 'user', ['mobile'], unique=True)
op.create_index(op.f('ix_user_name'), 'user', ['name'], unique=False)
op.create_index(op.f('ix_user_sex'), 'user', ['sex'], unique=False)
위의 코드 처럼 alembic revision --autogenerate 명령으로 생성된 versions의 python code에 sqlalchemy_utils를 사용하고 있는데, 정작 sqlalchemy_utils를 import 하지 않아서 에러가 발생하는 것이다.
이 script를 생성할 때 사용하는 "script.py.mako"를 수정하여 문제를 해결했다.
"""${message}
Revision ID: ${up_revision}
Revises: ${down_revision | comma,n}
Create Date: ${create_date}
"""
from alembic import op
import sqlalchemy as sa
import sqlalchemy_utils
${imports if imports else ""}
# revision identifiers, used by Alembic.
revision = ${repr(up_revision)}
down_revision = ${repr(down_revision)}
branch_labels = ${repr(branch_labels)}
depends_on = ${repr(depends_on)}
def upgrade():
${upgrades if upgrades else "pass"}
def downgrade():
${downgrades if downgrades else "pass"}
참고로, 기존에 데이터가 있는 경우 자동으로 암호화를 해주지는 않기 때문에, drop database로 DB를 날리고 새로 만드는 것을 추천한다.