Django | 인스타그램 클론 코딩(6) - 댓글 등록

Sua·2021년 2월 11일
0

Django

목록 보기
13/23
post-thumbnail
post-custom-banner

🙆 댓글 등록

이전 시간에는 장고로 게시물 등록, 표출 기능을 구현하고, 로그인 인증 데코레이터로 인가 과정을 구현하였습니다. 이번에는 게시물에 댓글 등록하는 기능을 구현하도록 하겠습니다. 마찬가지로 로그인 데코레이터를 사용해서 로그인된 권한있는 유저만이 댓글을 등록할 수 있도록 해보겠습니다.

🔎 댓글 등록 기능 분석

  1. 댓글에는 댓글을 작성할 게시물, 작성자, 내용, 생성 시간이 필요합니다.
  2. 댓글의 작성자는 이미 서비스에 가입된 유저여야 합니다.

🛠 App

django에서는 다루는 데이터를 기준으로 앱을 분리하여 관리합니다.
게시물을 저장해서 관리하는 테이블과, 댓글을 정리해서 관리하는 테이블은 분리되어 있을 것입니다.
따라서, 바라보는 관점에 따라 포스팅하는 게시물과 댓글은 같은 앱에서 관리할 수도, Posting앱과 Comment앱을 분리하여 관리할 수도 있습니다.
이번 위스타그램 프로젝트에서는 Posting앱에서 두 데이터 모두 관리해보도록 하겠습니다.

🖌 Model 작성

# posting/models.py

from django.db   import models
from user.models import User

(..생략)

class Comment(models.Model):
    content    = models.CharField(max_length=500)
    created_at = models.DateTimeField(auto_now_add=True)
    user       = models.ForeignKey('user.User', on_delete=models.CASCADE)
    posting    = models.ForeignKey('Posting', on_delete=models.CASCADE)

    class Meta:
        db_table = 'comments'  

Q. 같은 앱의 Posting 클래스를 참조하는 방식은?

Comment 클래스는 같은 앱에 있는 Posting 클래스를 참조하고 있습니다.
하나의 게시물의 여러 개의 댓글이 달릴 수 있으므로 PostingComment는 One-to-many의 관계입니다. 따라서 ForeignKey 필드를 사용합니다.

posting = models.ForeignKey('Posting', on_delete=models.CASCADE)

Q. 다른 앱(user)의 User 클래스를 참조하는 방식은?

Comment 클래스는 다른 앱(user 앱)있는 User 클래스를 참조하고 있습니다.
댓글의 작성자는 이미 서비스에 가입된 유저여야 하기 때문이죠.
한 명의 유저는 여러 개의 댓글을 달 수 있으므로 UserComment는 One-to-many의 관계입니다. 따라서 ForeignKey 필드를 사용합니다.

user = models.ForeignKey('user.User', on_delete=models.CASCADE)

💣 View 작성

댓글 등록 기능

로그인한 유저만 댓글 등록

지난 포스팅에서 만든 로그인 인증 데코레이터를 사용해서 로그인한 유저만 댓글을 등록할 수 있도록 했습니다.

class CommentView(View):
    @login_decorator
    def post(self, request):

로그인 데코레이터를 통과하면 토큰에 있는 유저 정보가 request.user에 담기게 됩니다. user 변수에 담도록 합니다.

try :
    data = json.loads(request.body)
    user = request.user

댓글을 등록할 게시물 있는지 확인

요청에서 받은 posting_id에 매칭되는 게시물이 있는지 확인합니다.
없다면 'POSTING_DOES_NOT_EXIST' 에러 메시지를 응답으로 보냅니다.
있다면 해당하는 그 객체를 찾아서 posting 변수에 담습니다.

if not Posting.objects.filter(id=posting_id).exists():
    return JsonResponse({'message':'POSTING_DOES_NOT_EXIST'}, status=404)
            
    posting = Posting.objects.get(id=posting_id)

댓글 표출 기능

특정 게시물에 대한 댓글만 표출(CommentSearchView)

댓글을 보여줄 때는 전체 댓글을 보여줄 필요가 없습니다. urlposting_id를 전달받아 해당 게시물에 대한 댓글만 보여주면 됩니다. 세부 정보에 대한 기능이므로 CommentSearchView를 새로 만들었습니다.

class CommentSearchView(View):
    def get(self, request, posting_id):

해당하는 게시물 있는지 확인

댓글을 등록할 때와 마찬가지로 posting_id에 매칭되는 게시물이 있는지 확인합니다.

if not Posting.objects.filter(id=posting_id).exists():
    return JsonResponse({'message':'POSTING_DOES_NOT_EXIST'}, status=404)

댓글 표출하기

하나의 게시물에 댓글이 여러 개일 수 있으므로 리스트에 담습니다. 리스트 컴프리헨션으로 각 댓글의 username, content, created_at 정보를 딕셔너리에 담고 리스트로 묶습니다.

comment_list = [{
    "username"  : User.objects.get(id=comment.user.id).username,
    "content"   : comment.content,
    "create_at" : comment.created_at
    } for comment in Comment.objects.filter(posting_id=posting_id)
]

return JsonResponse({'data':comment_list}, status=200)

👀 댓글 전체 View 코드

# posting/views.py

import json
from json.decoder import JSONDecodeError

from django.http  import JsonResponse
from django.views import View

from posting.models import Posting, Image, Comment  --> 수정
from user.models    import User
from user.utils     import login_decorator

(..생략)

class CommentView(View):
    @login_decorator
    def post(self, request):
        try :
            data = json.loads(request.body)
            user = request.user

            content    = data.get('content', None)
            posting_id = data.get('posting_id', None)

            if not (content and posting_id):
                return JsonResponse({'message':'KEY_ERROR'}, status=400)

            if not Posting.objects.filter(id=posting_id).exists():
                return JsonResponse({'message':"POSTING_DOES_NOT_EXIST"}, status=404)
            
            posting = Posting.objects.get(id=posting_id)
            
            Comment.objects.create(
                content = content,
                user    = user,
                posting = posting
            )

            return JsonResponse({'message':'SUCCESS'}, status=200)
        
        except JSONDecodeError:
            return JsonResponse({'message':'JSON_DECODE_ERROR'}, status=400)


class CommentSearchView(View):
    def get(self, request, posting_id):
        if not Posting.objects.filter(id=posting_id).exists():
            return JsonResponse({'message':'POSTING_DOES_NOT_EXIST'}, status=404)

        comment_list = [{
            "username"  : User.objects.get(id=comment.user.id).username,
            "content"   : comment.content,
            "create_at" : comment.created_at
            } for comment in Comment.objects.filter(posting_id=posting_id)
        ]

        return JsonResponse({'data':comment_list}, status=200)

📬 url 경로 지정하기

# posting/urls.py

from django.urls import path
from .views import (
    (..생략)
    CommentView, 
    CommentSearchView,
)

urlpatterns = [
    (..생략)
    path('/comment', CommentView.as_view()),
    path('/comment/search/<int:posting_id>', CommentSearchView.as_view()), 
    ]
profile
Leave your comfort zone
post-custom-banner

0개의 댓글