django 대댓글 구현하기

김혁준·2023년 7월 6일
0

django

목록 보기
15/18
class Recomment(models.Model):
    class meta:
        db_table = "Recomment"

    author = models.ForeignKey(
        User,
        on_delete=models.CASCADE,
        related_name="recomments",
    )
    article = models.ForeignKey(
        Article,
        on_delete=models.CASCADE,
    )
    comment = models.ForeignKey(
        Comment,
        on_delete=models.CASCADE,
    )
    recomment = models.TextField(
        max_length=300,
    )

    def __str__(self):
        return str(self.comment)

기본적으로 대댓글은 comment모델과 외래키로 연결해서 구현한다. comment모델 자체에 스스로 외래키를 걸거나 대댓글 모델을 새로 만드는 방법이 있는데 후자의 방법으로 구현했다. 아티클에 외래키를 걸음으로써 특정 아티클에 해당하는 대댓글만 불러올 것이고, 댓글에 외래키를 걸음으로써 댓글마다 대댓글이 다르게 보이도록 설정했다. 또한 작성자인 author 테이블을 유저모델에 외래키를 걸음으로써 누가 대댓글을 달았는지 유저모델을 통해 알수 있도록 했다.

serializers.py

class RecommentSerializer(serializers.ModelSerializer):

    # 댓글 조회 시리얼라이저-직렬화
    class Meta:
        model = Recomment
        fields = [
            "id",
            "comment",
            "recomment",
            "author",
            "is_author",
            "article",
        ]
   

시리얼라이저에서는 메소드필드(+추가정보)를 뺸 필수 필드만 넣었다. 실제로 대댓글에 달린 좋아요 갯수 등을 파악하고 싶으면 메소드필드로 like를 추가하고 get_likes_count 함수를 추가하면 된다.

대댓글에 페이지네이션을 적용할 예정이라 paginations.py를 만들었다.

from rest_framework.pagination import PageNumberPagination
class ReCommentPagination(PageNumberPagination):
    """CommentPagination: 대댓글 페이지네이션을 위한 클래스

    Attributes:
        page_size (int): 한 페이지에 몇 개의 대댓글을 담을지 결정합니다.
        page_query_param (str): 페이지 이름(query string에서 페이지를 지정할 파라미터) ex)http://127.0.0.1:8000/article/?comment_page=2 를 사용하면 2페이지로 이동
        max_page_size(int): 최대 페이지 수
    """

    page_size = 5
    page_query_param = "recomment_page"
    max_page_size = 100

프론트로부터 recomment_page 파라미터를 받으면 next와 prev에 대한 정보를 프론트한테 주고 요청마다 대댓글 5개씩 전달해준다.

class RecommentView(generics.ListCreateAPIView):
    permission_classes = [permissions.IsAuthenticatedOrReadOnly]
    pagination_class = ReCommentPagination
    serializer_class = RecommentSerializer
    queryset = None

    def get_queryset(self):
        queryset = Recomment.objects.filter(
            article_id=self.article_id, comment_id=self.comment_id
        )
        order = self.request.GET.get("order", None)
        if order == "1":
            return queryset.order_by("-like_count")
        if order == "2":
            return queryset.order_by("created_at")
        return queryset.order_by("-created_at")

    def get(self, request, *args, **kwargs):
        self.article_id = kwargs.get("article_id")
        self.comment_id = kwargs.get("comment_id")
        return super().get(request, *args, **kwargs)

    def post(self, request, article_id, comment_id):
        serializer = self.serializer_class(
            data=request.data, context={"request": request}
        )
        if serializer.is_valid():
            serializer.save(
                author=request.user, article_id=article_id, comment_id=comment_id
            )
            return Response(serializer.data, status=status.HTTP_200_OK)
        return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)

generics.ListCreateAPIView를 사용해 페이지네이션과 get,post 메소드를 구현했다. 기본적으로 아무파라미터도 안받으면 대댓글 생성순으로 자동정렬되고 order 파라미터를 1로 받으면 좋아요 순으로 정렬해서 데이터를 보내준다. 특정 대댓글을 얻으려면 아티클 id와 댓글 id가 필요하므로 get메소드에서 아티클 id와 댓글 id를 전달받기로 했고. super()로 오버라이딩해서 리턴값을 준다. post메소드의 경우도 마찬가지. get에서 serializer를 통해 원하는 데이터셋으로 데이터를 받을 예정이다.

class RecommentDetailView(APIView):
    def put(self, request, article_id, comment_id, recomment_id):
        recomment = get_object_or_404(Recomment, id=recomment_id)
        if request.user == recomment.author:
            serializer = RecommentSerializer(
                recomment, data=request.data, context={"request": request}
            )
            if serializer.is_valid():
                serializer.save()
                return Response(serializer.data, status=status.HTTP_200_OK)
            return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
        return Response(
            {"error": "본인이 작성한 대댓글만 수정할수 있습니다"}, status=status.HTTP_403_FORBIDDEN
        )

    def delete(self, request, article_id, comment_id, recomment_id):
        recomment = get_object_or_404(Recomment, id=recomment_id)
        if request.user == recomment.author:
            recomment.delete()
            return Response(
                {"message": "대댓글이 삭제되었습니다"}, status=status.HTTP_204_NO_CONTENT
            )
        return Response(
            {"error": "본인이 작성한 댓글만 삭제할수 있습니다"}, status=status.HTTP_403_FORBIDDEN
        )

put메소드와 delete메소드의 경우는 페이지네이션이 필요없으므로 apiView를 통해 구현했다. get_object_or_404 메소드를 통해 특정 대댓글을 지정하고 시리얼라이저를 통해 전달받은 데이터가 유효하면 저장하는 방식이다. delete의 경우 put과 같이 특정 대댓글이 지정되면, 요청한 사용자와 대댓글을 쓴 사용자가 같은지 확인이 되면 삭제가 되도록 구현했다. put도 둘다 같은지 먼저 검증했다. -끝-

profile
스프링 개발자 지망생입니다

0개의 댓글