TIL(22/11/29)

김규현·2022년 12월 1일
0

💻 Today I Learned

최종 팀 프로젝트가 시작되었고, 오늘은 S.A 작성 및 API, ERD 설계를 마친 후 팀원들과 같이 모델링을 끝내고 migrations를 하는 과정에서 아래와 같은 import 에러가 발생했다.

검색을 해보니 circular dependency 문제로 모델을 import 하는 과정에서 에러가 발생한 것 같다.

circular dependency Error

circular dependency를 해석하자면 circular(순환) dependency(의존성) 때문에 생기는 문제이다.
보통 ForeignKey를 사용할 때 자주 발생된다고 한다.

여기서 의존성이란 예를 들어, 아래의 그림에서 A.py가 필요에 의해서 B.py를 import를 하게되면 A.py는 B.py에 의존성을 가지게 되는 것이다. 마찬가지로 B.py 역시 필요에 의해 C.py를 import 하게되면 B.py는 C.py에 의존성을 가지게 된다.

만약 A->B->C->D->A 이와 같이 의존성을 가지게 되면 파일을 import 하는 과정이 무한으로 반복되어 내가 마주했던 import error가 발생하게 되는 것이다.

이유는 Django의 경우 실행 시 python 코드를 순서대로 읽으면서 필요한 소스를 import 해오는데
A.py 파일 안에서 import B를 만나면 B.py로 이동하여 코드를 읽고 가져온다.

마찬가지로 B.py 역시 import C를 만나면 C.py로 이동하여 코드를 읽고 가져오게 되는데 의존성을 가지기 시작한 A.py가 돌고 돌아 D.py에서 A.py의 의존성을 가지게 되면 끝이 없는 순환 구조를 가지게 된다.

문제를 해결하기 위해 다른 블로그를 검색해보니 서로 다른 파일에 위치한 두 모델이 서로를 ForeignKey 하려고 할 때의 예시를 들면서 설명해주었지만 나의 상황은 아래와 같이 서로 다른 파일에 있는
다른 모델을 각각 ForeignKey하는 경우이다.

# users/models.py
from workshops.models import Hobby

class User(AbstractBaseUser):
    email = models.EmailField("이메일" ,max_length=255, unique=True) 
    nickname = models.CharField("닉네임", max_length=15, unique=True)
    hobby = models.ForeignKey(Hobby, on_delete=models.CASCADE)

# workshops/models.py
from users.models import User

class Hobby(models.Model):
    category = models.CharField(max_length=10)
    
class Workshop(models.Model):
	category = models.ForeignKey(Hobby, on_delete=models.CASCADE)
	title = models.CharField(max_length=20)
	host = models.ForeignKey(User, on_delete=models.CASCADE, related_name='workshop_host')
	participant = models.ManyToManyField(User, related_name='member', symmetrical=False)
	likes = models.ManyToManyField(User, related_name='workshop_likes')

가독성을 위해 필드를 몇가지만 가져왔다. 서로 다른 위치에 있는 각각의 모델이 1:1로 의존성을 가지는 것이 아니라 User 모델은 workshops App의 Hobby 모델이 필요, Workshop 모델은 users App의 User 모델이 필요하여 삼각 관계이다.

하지만 서로 필요한 모델은 달라도 import 해야하는 파일은 1:1로 매칭되기 때문에 import 에러가 발생한 것 같다.

해결방법

결국 서로 다른 파일이 의존성을 가져 서로 import 하려고 하여 반복되는 문제인데 아래와 같이 해결할 수 있다.

# users/models.py
#from workshops.models import Hobby -> 순환이 반복되지 않게 한 쪽에서 import를 없앤다.

class User(AbstractBaseUser):
    email = models.EmailField("이메일" ,max_length=255, unique=True) 
    nickname = models.CharField("닉네임", max_length=15, unique=True)
    
    # ForeignKey 필드에서 모델명 대신 'APP_name.Model_name'을 입력해준다. 
    # ex) models.ForeignKey(articles.Article, on_delete=models.CASCADE)
    hobby = models.ForeignKey('workshops.hobby', on_delete=models.CASCADE) 

# workshops/models.py
from users.models import User

class Hobby(models.Model):
    category = models.CharField(max_length=10)
    
class Workshop(models.Model):
	category = models.ForeignKey(Hobby, on_delete=models.CASCADE)
	title = models.CharField(max_length=20)
	host = models.ForeignKey(User, on_delete=models.CASCADE, related_name='workshop_host')
	participant = models.ManyToManyField(User, related_name='member', symmetrical=False)
	likes = models.ManyToManyField(User, related_name='workshop_likes')
profile
웹개발 회고록

0개의 댓글