개발기록장

[Django] 인스타그램 클론 코딩(5) - 좋아요(Like) 기능 구현 본문

TIL/Django

[Django] 인스타그램 클론 코딩(5) - 좋아요(Like) 기능 구현

yangahh 2021. 2. 9. 21:50

 

 

[이전 글]

[Django] 인스타그램 클론 코딩(4) - 게시물과 댓글 C.R.U.D (+ RESTful API에서 Update에는 무슨 메소드를 써야하는가...)

 

[Django] 인스타그램 클론 코딩(4) - 게시물과 댓글 C.R.U.D (+ RESTful API에서 Update에는 무슨 메소드를

[이전 글] [Django] 인스타그램 클론 코딩(3) - Authorization Decorator 만들고 활용하기 [Django] 인스타그램 클론 코딩(3) - Authorization Decorator 만들고 활용 [이전 글] devvvyang.tistory.com/41 [Django..

devvvyang.tistory.com

 

[다음 글]

[Django] 인스타그램 클론 코딩(6) - 팔로우(follow) 기능 구현

 

[Django] 인스타그램 클론 코딩(6) - 팔로우(follow) 기능 구현

[이전 글] [Django] 인스타그램 클론 코딩(5) - 좋아요(Like) 기능 구현 [Django] 인스타그램 클론 코딩(5) - 좋아요(Like) 기능 구현 [이전 글] [Django] 인스타그램 클론 코딩(4) - 게시물과 댓글 C.R.U.D (+ RE..

devvvyang.tistory.com

 

 

인스타그램의 Like(좋아요) 기능은 users 테이블과 posts 테이블의 M:N 관계로 구현할 수 있다.

한 사람이 여러 게시물에 Like를 누를 수 있고, 하나의 게시물 입장에서 여러 사람에게 Like를 받을 수 있기 때문이다.

 

이 관계에서 posts가 기준이 되는 기능이므로 post/models.py의 Post class에 ManyToManyField를 추가해주었다.


1 - 1. user/models.py

from django.db   import models

class User(models.Model):
    email      = models.EmailField(max_length=50, unique=True) 
    name       = models.CharField(max_length=20, unique=True) 
    phone      = models.CharField(max_length=15, unique=True)
    password   = models.CharField(max_length=300)
    created_at = models.DateTimeField(auto_now_add=True)
    updated_at = models.DateTimeField(auto_now=True)

    class Meta:
        db_table = 'users'

1 - 2. post/models.py

from django.db   import models

from user.models import User

class Post(models.Model):
    user        = models.ForeignKey('user.User', on_delete=models.CASCADE) 
    image_url   = models.URLField(max_length=500)
    content     = models.TextField(null=True)
    created_at  = models.DateTimeField(auto_now_add=True)
    updated_at  = models.DateTimeField(auto_now=True)
    liked_users = models.ManyToManyField('user.User', through='Like', related_name='liked_posts')

    class Meta:
        db_table = 'posts'

(생략..)

class Like(models.Model):
    user = models.ForeignKey('user.User', on_delete=models.CASCADE)
    post = models.ForeignKey('Post', on_delete=models.CASCADE)

  • 위에서 설명한 대로 liked_users라는 attribute에 ManyToManyField를 이용하여 M:N 관계를 설정하였다.

  • liked_users라는 필드는 물리적으로 생기지 않는 필드이며 단지 두 테이블 사이에서 forward-many-to-many manager 역할을 한다.

    ** ManyToManyField에 대한 자세한 설명은 여기를 참고!!

  • 이때 through 옵션을 지정하여 Like 라는 테이블을 중간 테이블로 사용하게 하였고,

  • User 입장에서 역참조를 쉽게 하기 위해 related_name도 'liked_posts'로 지정해주었다.

 

2. post/views.py

import json

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

from .models      import Post, Comment, Like
from user.models  import User
from user.utils   import login_decorator

(생략...)

class LikeView(View):
    @login_decorator
    def post(self, request):
        data    = json.loads(request.body)
        user    = request.user
        post_id = data.get('post', None)

        # KEY_ERROR check
        if not post_id:
            return JsonResponse({'message': 'KEY_ERROR'}, status=400)

        # valid post check
        if not Post.objects.filter(id=post_id).exists():
            return JsonResponse({'message': 'INVALID_POST'}, status=400)

        post = Post.objects.get(id=post_id)

        if post.liked_users.filter(id=user.id).exists():
            post.liked_users.remove(user)
            message = 'Cancle'
        else:
            post.liked_users.add(user)
            message = 'Like'

        like_count = post.liked_users.count()

        return JsonResponse({'message': message, 'like_count': like_count}, status=200)

  • request body에서 'Like' 대상인 post_id를 받아온다고 가정하고 코드를 짰다.

  • 'Like'기능은 like라는 이름의 중간테이블에 'Like'를 누른 user_id와 그 대상인 post_id가 기록되는 형식으로 구현된다.

  • user가 이미 특정 게시물에 'Like'를 한 상태에서 또 한 번 'Like'를 누른다면 Like를 해제하는 것이므로 중간 테이블에서 해당 데이터를 삭제해주고, 아직 'Like'를 한 상태가 아니라면 데이터를 추가해준다.

  • 중간 테이블(like)에 데이터를 추가하고 삭제하는 것은 forward_many_to_many_manager인 'post.liker_user.'를 이용하여 간결하게 구현할 수 있다.

 

3. post/usls.py

'Like' 기능 엔드포인트 추가

from django.urls import path

from .views import (
    (생략..)
    LikeView, 
)

urlpatterns = [
    (생략..)
    path('/like', LikeView.as_view()),
]