TIL no.36 Westagram Posting & Comment

7187 단어 TILdjangoTIL

이번에는 로그인을 통해 게시물을 작성과 추가기능을 구현하고 게시물에 댓글과 대댓글을 달아보는 기능을 만들어 보겠다.

Posting

제일 먼저 그동안을 users라는 앱에 작성했지만 데이터의 종류가 달라지는 시점에서 앱을 분리하는 것이 좋으니 posts라는 앱을 생성해서 따로 관리하겠다. 앱을 새로 만들었으니 model을 작성하겠다.

#posts.models.py

class Post(models.Model):
    user         = models.ForeignKey('users.User', on_delete=models.CASCADE)
    title        = models.CharField(max_length=200)
    content      = models.TextField(max_length=2000, null=True)
    image_url    = models.CharField(max_length=1000, null=True)
    created_time = models.DateTimeField(auto_now_add=True)

    class Meta:
        db_table = "posts"

작성자를 확인하기 위해서 user라는 column을 외래 키로 users.User를 외래 키로 지정했고 테이블명을 posts로 지정하였다.

posts.views.py

class PostView(View):
    @token_reader
    def post(self, request):
        try:
            data = json.loads(request.body)
            
            Post.objects.create(
                user        = request.user,
                title       = data.get('title'),
                content     = data.get('content'),
                image_url   = data.get('image_url'),
            )
            
            return JsonResponse({"MESSAGE":"SUCCESS"}, status=201)

        except KeyError:
            return JsonResponse({"message": "KEY_ERROR"}, status=400)
    
    def get(self, request):
        posts  = Post.objects.all()
        result = []
        for post in posts:
            result.append({
                'user_name'   : post.user.name,
                'content'     : post.content,
                'image_url'   : post.image_url,
                'title'       : post.title,
                'created_time': post.created_time
            }) 

        return JsonResponse({"MESSAGE": result}, status=200)
        ```



게시물을 작성할 때는 post 메서드를 사용하고 기존에 작성했던 로그인 데코 레이터를 사용해서 인가해주고 json 형식을 받은 request를 불러와 쿼리 셋을 사용하여 데이터를 생성해 준다. 기존에 작성되었던 코드를 읽어올 경우에는 get 메서드를 사용해서 post 테이블에 저장되어 있던 데이터를 모두 불러온다.

여기서 더 나아가 추가적인 기능을 만들어봤는데 로그인을 통해 자신이 작성한 게시물을 수정, 삭제 혹은 자신이 작성한 게시글을 읽어오고 싶을 때의 기능을 구현해 보았다.


class PostDetailView(View):
@token_reader
def delete(self, request, post_id):
try:
if not Post.objects.filter(id=post_id, user_id=request.user).exists():
return JsonResponse({"message": "NOT_EXIST_POST"}, status=400)

        Post.objects.filter(id=post_id, user_id=request.user).delete()
        return JsonResponse({"MESSAGE": "SUCCESS_DELETE"}, status=200)

    except KeyError:
        return JsonResponse({"message": "KEY_ERROR"}, status=400)


@token_reader
def patch(self, request, post_id):
    try:
        data=json.loads(request.body)
        if not Post.objects.filter(id=post_id, user_id=request.user).exists():
                return JsonResponse({"message": "NOT_EXIST_POST"}, status=400)
        
        post = Post.objects.get(id=post_id, user_id=request.user)
        
        post.title        = data.get('title', post.title)
        post.content      = data.get('content', post.content)
        post.image_url    = data.get('image_url', post.image_url)
        post.created_time = data.get('created_time', post.created_time)

        post.save()
        
        return JsonResponse({"MESSAGE": "SUCCESS_UPDATE"}, status=200)
    
    except KeyError:
        return JsonResponse({"message": "KEY_ERROR"}, status=400)

@token_reader
def get(self, request):
    result=[]
    posts = Post.objects.filter(user_id=request.user)
    if not Post.objects.filter(user_id=request.user):
        return JsonResponse({"message" : "not_exits"}, status=400)
    
    for post in posts:
        result.append({
            "name"         : post.user.name,
            "title"        : post.title,
            "content"      : post.content,
            "created_tiem" : post.created_time
        })
    
    return JsonResponse({"MESSAGE": result}, status=200)
상위 코드를 보면 PostDetailView라는 새로운 클래스를 생성하였다. 이유는 url를 나누어서 사용하기 위함이다. 눈여겨봐야 할 부분은 delete 함수에 보지 못했던 parameter가 있다. 왜냐하면 delete 메 서드는 request의 body를 사용하지 않기 때문에 url에서 새로운 parameter를 부여서 함수 내부에서 사용한다.
path('/post/detail', PostDetailView.as_view()),
path('/post/detail/<int:post_id>', PostDetailView.as_view()),```

url은 상위처럼 나누었다.

Comments

이번에는 게시물에 달린 댓글을 작성해 볼건데 이때 댓글에 댓글인 대댓글 까지 구현하기 위해서 model을 작성해볼 것이다.

#posts.models.py
class Comment(models.Model):
    user       = models.ForeignKey("users.User", on_delete=models.CASCADE)
    post       = models.ForeignKey("Post", on_delete=models.CASCADE) 
    content    = models.CharField(max_length=500, null=True)
    comment    = models.ForeignKey("self", on_delete=models.SET_NULL, null=True, related_name='recomment')
 
    
    class Meta:
        db_table = "comments"

comment라는 column은 외래 키로 자기 자신을 참조하며, 기존과 달리 cascade 속성이 아니 set_null이다. 만약 대댓글이 삭제될 경우 연쇄 적으로 모두 삭제되는 것이 아닌 자기 자신만 삭제된다. 그리고 related_name 속성을 사용하여 데이터베이스가 역참조 시 헷갈리지 않게 이름을 지정해 준다.

#posts.views.py
class CommentView(View):
    @token_reader
    def post(self, request):
        try:
            data = json.loads(request.body)
            
            if not Post.objects.filter(title=data['title']).exists():
                return JsonResponse({"message": "NOT_EXIST_POST"}, status=400)

            Comment.objects.create(
                user       = request.user,
                post       = Post.objects.get(title=data['title']),
                content    = data.get('content'),
                comment_id = data.get('comment_id')
            )
            return JsonResponse({"MESSAGE":"SUCCESS"}, status=201)
        
        except KeyError:
            return JsonResponse({"message": "KEY_ERROR"}, status=400)

    def get(self, request, posting_id):
        result   = []
        
        comments =  Comment.objects.filter(comment_id=None)
        for comment in comments:
            recoments = []
            if Comment.objects.filter(comment_id = comment.id).exists():
                for recoment in Comment.objects.filter(comment_id = comment.id):
                    recoments.append({
                        "user"       : recoment.user.name,
                        "post_title" : recoment.post.title
                    })
        
            result.append({
                "comment_id" : comment.id,
                "user_name"  : comment.user.name,
                "post_title" : comment.post.title,
                "content"    : comment.content,
                "recomment"  : recoments if True else None
            })
        return JsonResponse({"MESSAGE": result}, status=200)

상위 코드에서 get 메서드를 사용하여 읽어오는데 또 parameter가 존재한다. 당연히 url을 나누어서 아용하고 상위의 코드는 댓글 안에 대댓글을 불러오게 하는 방법이다. 출력 결과는 다음과 같다.

좋은 웹페이지 즐겨찾기