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를 날리고 새로 만드는 것을 추천한다.