Django (1215, ‘cannot add foreign key constraint’) 오류 해결

이미 만들어진 Django 프로젝트의 ORM을 새로운 데이터베이스(DB)에 migration 할 때 종종 접하게 되는 대표적이 오류가 django.db.utils.IntegrityError: (1215, ‘Cannot add foreign key constraint’) 오류이다.

그 의미를 해석하자면 말그대로, ORM을 DB로 옮기는 과정에서(주로 Table을 생성하거나 수정) foreign key 제약 조건을 추가하는 작업을 DB 단에서 수행하지 못하였다는 뜻

이 문제가 발생하는 원인에는 여러가지 이유가 있겠지만, 크게 아래와 같이 두가지 경우가 실무단에서 가장 자주 발생하는 문제이다.

Django ORM에서 Foreign key 관계로 연결되어 있는 Model들이 DB 상에서 서로 다른 Collation을 가지거나 Engine을 가진 Table로 표현된 경우

MySQL/Aurora 기준으로 서로 다른 Engine(InnoDB, MyISAM 등)이거나 테이블에서 사용하는 문자열 인코딩을 의미하는 Collation이 서로 다르거나 할 경우, 그 두개의 테이블을 Foreign Key 관계로 연결하는 과정에서 문제가 생기는 경우이다.

일반적인 경우에 같은 DB Scheme 아래에서 서로 다른 Collation이나 Engine을 가지는 테이블이 생성되는 경우는 드물지만, 프로젝트를 진행하는 도중에 어떠한 사유에 의해서건 테이블의 Engine을 변경하였거나 Collation을 변경하여 Foreign Key로 연결된 테이블간에 서로 다른 Engine/Collation을 가지고 있는게 아닌지 확인이 필요하며 확인 후 다른 부분이 있다면 동일한 Engine/Collation으로 통일하여 테이블들을 변경하거나 다시 생성하면 해당 문제가 해결된다.

Django App(Model) 간 Circular Import Dependency가 존재하는 경우

INSTALLED_APPS = [
    'membership',
    'posts',
]

Django 프로젝트 내에서 위와 같은 App 이 존재하고 가정했을 때, 아래와 같이 각 App 내에서 서로가 서로를 참조하는 형태로 설계가 되어 있다면,

  1. membership/models.py에서 from posts.models import Post를 선언
  2. 파이썬 인터프리터는 위 구문에 따라서 Post를 import하기 위해서 post/models.py로 갔더니 from membership.models import Member라는 구문 만나게 된다.
  3. 파이썬 인터프리터는 2의 구문에 따라 다시 membership/models.py로 가서 다시 from posts.models import Post 를 마주하게 된다.

서로가 서로를 무한히 참조하는 루프에 빠지게 된다.

어떤 App에 속한 Model 정의가 채끝나기도 전에 다시 해당하는 App 자신이 참조되기 때문에 발생한 문제이다.

보통 이러한 경우 모델에서 ForeignKey 필드를 선언 할 때, 참조할 대상 클래스를 직접 넣는 대신 model의 이름을 string 형태로 아래와 같이 직접 넣는 형태로 작성하면 해결이 된다.

이 방법 대신,

from posts.models import Post

class MemeberLike(models.Model):
    user = models.ForeignKey(Post, on_delete=models.CASCADE)

아래와 같이.

class MemeberLike(models.Model):
    user = models.ForeignKey('posts.Post', on_delete=models.CASCADE)

하지만, 이렇게 작성하더라도 문제가 발생하는 경우는 여전히 존재할 수 있으니 이미 작성된 Django Project 바로 새로운 데이터베이스에 DB migration을 최초로 진행 할 때이다. Django는 Migration을 진행 할 때INSTALLED_APPS 에 입력되어 있는 App의 Model들을 순차적으로 DB 상의 테이블로 생성 하는데, 테이블을 생성하는 과정에서 위와 같은 Circular Dependency가 존재하다보니 아직 존재하지 않는 테이블을 Foreign Key로 참조하는 Model들이 발생하고 그 Model들이 발견되는 즉시 1215, ‘cannot add foreign key constraint’ 에러를 출력하며 Migration 작업을 중단하게 되는 것 이다.

이 경우에 대한 해결책 역시 생각보다 간단하다. Migration 과정에서 오류가 나는 App의 Model 들을 확인하고 Circular Dependency가 존재하는(즉, 위의 예시에서와 같이 Foreign Key 필드 정의 시 대상 모델의 이름을 클래스 대신 string 형태로 넣은) 모든 필드를 주석 처리하고 migration을 진행 한 이후에 성공적으로 첫번째 migration이 완료되어 원래 Foreign Key 필드에서 참조하려는 모든 Model들의 테이블이 정상 생성된 이후에 주석을 다시 해제하고 makemigrations 명령어 이후 다시 migrate 명령어를 통해 migration을 진행 하는 것.

이렇게 진행 할 경우 과정 상으로는 Foreign Key에서 참조 대상이 되는 모든 Model의 테이블이 생성된 이후에, 해당 테이블을 참조하기 위한 Foreign Key 필드가 추가되는 형태이기 때문에 Django 의 migrate 명령어를 통해서 오류 없이 정상적인 migration이 가능해진다.