한 줄 요약
*강등시키려면 *Alembic의 ForeignKey 필드에 이름을 입력해야 합니다.
*외래 키 필드는 다른 테이블의 기본 키를 참조하는 테이블 필드를 나타냅니다.
*Alembic은 Python용 데이터베이스 마이그레이션 도구입니다.
*다운그레이드는 이전 버전으로 돌아가는 것을 의미합니다.
문제 상황
상위 엔터티를 삭제하면 하위 엔터티도 삭제할 수 있습니다.
ondelete=”CASCADE” 옵션을 parent_id 하위 필드에 추가했습니다.
class Child(Base):
__tablename__ = "child"
id = sa.Column(UUID(as_uuid=True), primary_key=True)
parent_id = sa.Column(
UUID(as_uuid=True),
ForeignKey("parent.id", ondelete="CASCADE"), # <-- 이 부분
nullable=False,
index=True,
)
Alembic 버전 파일도 생성되어 마이그레이션이 정상적으로 진행되었습니다!
def upgrade() -> None:
# ### commands auto generated by Alembic - please adjust! ###
op.drop_constraint('child_parent_id_fkey', 'child', type_='foreignkey')
op.create_foreign_key('child_parent_id_fkey', 'child', 'parent', ('parent_id'), ('id'), ondelete="CASCADE")
# ### end Alembic commands ###
def downgrade() -> None:
# ### commands auto generated by Alembic - please adjust! ###
op.drop_constraint('child_parent_id_fkey', 'child', type_='foreignkey')
op.create_foreign_key('child_parent_id_fkey', 'child', 'parent', ('parent_id'), ('id'))
# ### end Alembic commands ###
그러나 버전을 다운그레이드하면 다음과 같은 오류 메시지가 나타납니다!?
sqlalchemy.exc.CompileError: Can't emit DROP CONSTRAINT for constraint ForeignKeyConstraint(<sqlalchemy.sql.base.DedupeColumnCollection object at 0xffff80c086d0>, None, table=Table('child', MetaData(), schema=None)); it has no name
해결
문제의 원인은 버전 파일에서 Alembic이 임의로 지정한 이름(child_parent_id_fkey)이 실제 ForeignKey 필드에 정의되어 있지 않기 때문입니다.
SQLAlchemy는 이름이 정의되지 않았기 때문에 외래 키 제약 조건에서 DROP CONSTRAINT 문을 실행할 수 없습니다! (깨달음 포인트)
이 문제를 해결하려면 ForeignKey 필드를 정의할 때 이름을 직접 지정해야 합니다. B. 아래와 같이 name=”child_parent_id_fkey”.
class Child(Base):
__tablename__ = "child"
id = sa.Column(UUID(as_uuid=True), primary_key=True)
parent_id = sa.Column(
UUID(as_uuid=True),
ForeignKey("parent.id", name="child_parent_id_fkey", ondelete="CASCADE"),
nullable=False,
index=True,
)
이를 반영하도록 버전 파일의 코드를 변경할 수 있지만 DB 볼륨을 완전히 삭제하고 다시 마이그레이션하십시오.
함께 보면 좋은 공문서