- 게시글에 대한 댓글은 ForeignKey를 이용해 간단하게 구현할 수 있다. 하지만 댓글에 대한 댓글, 또 그 댓글에 대한 대댓글 등 중첩으로 댓글을 구현하기 위해서는 model을 생성할 때 자신(댓글)을 부모로 참조하여 구성해야 한다.
- 데이터베이스는 입력 순서대로 id를 부여하고 데이터를 저장하기 때문에 댓글(부모)에 대한 댓글(자식)을 작성할 경우, 자식 댓글은 부모 댓글의 밑이 아닌 테이블의 가장 아래에 저장하게 된다.
- Front-end에서 댓글의 queryset을 부모-자식 관계에 맞게 화면에 출력하거나, Back-end에서 관계에 맞게 정리하여 Front-end에 전달할 수 있다.
- 이 포스트는 Back-end에서 중첩 댓글을 순서 및 관계에 맞게 정리하여 전달하는 방법을 작성하였다.
# models.py
class Comment(models.Model):
product = models.ForeignKey(Product, related_name='comments', on_delete=models.CASCADE)
user = models.ForeignKey(User, related_name='users', on_delete=models.CASCADE)
parent = models.ForeignKey('self', related_name='reply', on_delete=models.CASCADE, null=True, blank=True)
body = models.CharField(max_length=100)
created = models.DateTimeField(auto_now_add=True)
- 최상위 댓글(parent=None)을 이용해 하위 댓글을 불러온다.
# views.py
class CommentList(ListCreateAPIView):
serializer_class = CommentSerializer
def get_queryset(self):
queryset = Comment.objects.filter(parent=None)
...
# serializers.py
# to_represenstation을 오버라이딩해서 화면에 출력하는 방법
class RecursiveSerializer(serializers.Serializer):
def to_representation(self, instance):
serializer = self.parent.parent.__class__(instance, context=self.context)
return serializer.data
class CommentSerializer(serializers.ModelSerializer):
reply = serializers.SerializerMethodField()
# reply = RecursiveSerializer(many=True, read_only=True)
product = serializers.SlugRelatedField(queryset=Product.objects.all(), slug_field='name')
class Meta:
model = Comment
fields = ('id', 'user', 'product', 'parent', 'body', 'reply')
def get_reply(self, instance):
# recursive
serializer = self.__class__(instance.reply, many=True)
serializer.bind('', self)
return serializer.data
- instance.reply를 통해 자식 댓글을 불러오고, self.__class__를 통해 직렬화가 이루어진다.
- 직렬화 과정 중 self.__class__ (직렬화) -> get_reply 호출 -> self.__class__ (직렬화) -> get_reply 호출... 재귀가 일어난다.
- serializer.bind('', self)는 직렬화된 자식을 부모에게(필드 인스턴스 - reply) 연결한다.
- 재귀를 이용하기 때문에 댓글의 양이 많고 복잡하게 얽혀있을 경우 페이지를 출력하기 위해 많은 자원을 소모할 수 있다.
- Third party library인 drf-serializer-cache를 이용해 직렬화 과정중 발생하는 속도 저하를 개선시킬 수 있다.
[참고]
drf-serializer-cache 0.3.3 - https://pypi.org/project/drf-serializer-cache/