최근 Tcoops of Django 라는 책을 보며 Django를 처음부터 다시 복습하고 있다. 그리고 책에서 배운 것을 토대로 간단한 블로그를 만들어 보고 있는데, 특히 아래의 부분들을 중점적으로 신경쓰고 있다.
- Django에서 제공하는 다양한 Generic class-based-view들을 최대한 활용하여 코드를 간결하게 하자.
- Django에서 제공하는 Form을 이용하여, Form과 관련된 로직들을 View와 분리하자.(책의 말을 빌리자면, View는 오직 프레젠테이션(presentation) 로직만을 처리해야 한다. 비지니스(business) 로직은 Model이나 Form으로 이동시키자.)
- 또 Form을 이용하여 모든 경우의 입력 데이터에 대하여 유효성 검사를 하자.
- Class, Mixin의 상속을 통해 코드의 재사용성을 극대화하자.
- username없이 email만으로도 인증이 가능한 User 모델을 만들어 보자.
블로그에 게시물에 대한 좋아요 기능도 넣고 싶어, 아래와 같이 Like
라는 모델을 생성하고, Post
모델을 수정하였다.
# posts/models.py
from django.db import models
from django.conf import settings
from likes.models import Like
class Post(models.Model):
user = models.ForeignKey(settings.AUTH_USER_MODEL)
title = models.CharField(max_length=20)
content = models.TextField()
image = models.ImageField(
blank=True,
null=True,
)
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
# 아래 필드 추가
like_user_set = models.ManyToManyField(
settings.AUTH_USER_MODEL,
related_name="like_post_set",
through=Like,
)
# likes/models.py
from django.db import models
from django.conf import settings
from posts.models import Post
class Like(models.Model):
user = models.ForeignKey(settings.AUTH_USER_MODEL)
post = models.ForeignKey(Post)
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
그러고 migrate를 하려고 하니, 아래와 같은 에러가 발생하였다.
전혀 생각지도 못한 에러라 매우매우 당황했다. 오타가 있겠지 싶어 자세히 봤지만, 안타깝게도 틀린 글자도 없었다. 다른 코드에서도 항상 이렇게 import 하였는데, 왜 여기서만 에러가 뜨는건지 도무지 이해가 안 됐었다.
한참을 구글링한 끝에, 내가 현재 circular import dependencies 문제를 겪고 있음을 알 수 있었다.
Django는 settings.py
안의 INSTALLED_APPS
라는 배열에 있는 app들을 위에서부터 순서대로 정의한다.
# settings.py
# Application definition
INSTALLED_APPS = [
# 생략
'users',
'posts',
'likes',
따라서 users
=> posts
=> likes
순서로 app을 정의하게 되는데, 이 때 posts
라는 app을 정의하고 있다고 하자.
posts/models.py
에서from likes.models import Like
를 만나게 된다.Like
를 import하기 위해서likes/models.py
로 가였더니from posts.models import Post
를 만나게 된다.다시
posts/models.py
로 가서.. 응?
이렇게 무한반복이 된다.
어떤 app의 정의가 채끝나기도 전에 다시 해당하는 app 자신이 참조되기 때문에 발생한 문제이다.
그렇다고 서로 다른 app에서 서로의 model를 참조하는 것이 불가능한 것은 아니다.
Django 공식 문서를 보면 아래와 같이 해결법이 나와있다.
If you need to create a relationship on a model that has not yet been defined, you can use the name of the model, rather than the model object itself
문서에 나와있는 대로, ForeignKey
, ManyToManyField
안에 model 객체가 아닌 model의 이름을 문자열로 넣어주었더니 정상적으로 작동하였다.
# posts/models.py
like_user_set = models.ManyToManyField(
settings.AUTH_USER_MODEL,
related_name="like_post_set",
through="likes.Like",
)
# likes/models.py
post = models.ForeignKey("posts.Post")
책을 보며 공부하면서, Django라는 프레임워크에 대해 더욱 자신감이 생기고, 조금씩 거만해질수도 있는 찰나, 아직도 내가 모르는 부분은 어마어마하게 많다는 것을 다시 한 번 느낄 수 있는 좋은 계기였다.