이전 시간에는 장고로 게시물 등록, 표출 기능을 구현하고, 로그인 인증 데코레이터로 인가 과정을 구현하였습니다. 이번에는 게시물에 댓글 등록하는 기능을 구현하도록 하겠습니다. 마찬가지로 로그인 데코레이터를 사용해서 로그인된 권한있는 유저만이 댓글을 등록할 수 있도록 해보겠습니다.
댓글을 작성할 게시물
, 작성자
, 내용
, 생성 시간
이 필요합니다.작성자
는 이미 서비스에 가입된 유저여야 합니다. django에서는 다루는 데이터를 기준으로 앱을 분리하여 관리합니다.
게시물을 저장해서 관리하는 테이블과, 댓글을 정리해서 관리하는 테이블은 분리되어 있을 것입니다.
따라서, 바라보는 관점에 따라 포스팅하는 게시물과 댓글은 같은 앱에서 관리할 수도, Posting
앱과 Comment
앱을 분리하여 관리할 수도 있습니다.
이번 위스타그램 프로젝트에서는 Posting
앱에서 두 데이터 모두 관리해보도록 하겠습니다.
# 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'
Comment
클래스는 같은 앱에 있는 Posting
클래스를 참조하고 있습니다.
하나의 게시물의 여러 개의 댓글이 달릴 수 있으므로 Posting
과 Comment
는 One-to-many의 관계입니다. 따라서 ForeignKey
필드를 사용합니다.
posting = models.ForeignKey('Posting', on_delete=models.CASCADE)
Comment
클래스는 다른 앱(user 앱)있는 User
클래스를 참조하고 있습니다.
댓글의 작성자
는 이미 서비스에 가입된 유저여야 하기 때문이죠.
한 명의 유저는 여러 개의 댓글을 달 수 있으므로 User
와 Comment
는 One-to-many의 관계입니다. 따라서 ForeignKey
필드를 사용합니다.
user = models.ForeignKey('user.User', on_delete=models.CASCADE)
지난 포스팅에서 만든 로그인 인증 데코레이터를 사용해서 로그인한 유저만 댓글을 등록할 수 있도록 했습니다.
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)
댓글을 보여줄 때는 전체 댓글을 보여줄 필요가 없습니다. url
로 posting_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)
# 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)
# 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()),
]