My Profile Photo

DongChanS's blog


수학과 학생의 개발일지


인스타 클론코딩 3, Posts앱 (3) - Like 기능 만들기

1) Posts App

1-1. OverView

  1. Post 모델 작성

    • 1:N relation

    • 다른 유저가 Post에 ‘좋아요’ 기능을 넣어주고 싶음.

      => M:N relation

  2. M:N relation

    • 중간관리자 테이블의 필요성

    • ManyToManyField
    • 두 테이블을 연결하는 방법

1-2. Post와 유저를 연결

  1. 유저들의 정보를 담는 데이터베이스 : User 클래스, get_user_model()

  2. User 클래스와 Post 클래스의 관계

    • 유저 한명이 여러 Post에 ‘좋아요’ 버튼을 클릭함.
    • 포스트 하나도 여러 유저에게 ‘좋아요’ 버튼을 받을 수 있음.
    • 한쪽이 완전히 소유하는 관계가 아님

    => 이러한 경우에는 M:N relation을 적용하는게 좋다.

2) M:N relations (Many-to-Many relationship)

2-1. 설명

위키피디아 참고

For example, think of A as Authors, and B as Books. An Author can write several Books, and a Book can be written by several Authors.

In a relational database management system, such relationships are usually implemented by means of an associative table (also known as join table, junction table or cross-reference table), say, AB with two one-to-many relationships A -> AB and B -> AB. In this case the logical primary key for AB is formed from the two foreign keys (i.e. copies of the primary keys of A and B).

이전 글에 적었던 Post와 User의 소유관계와는 달리, 한 포스트가 여러 유저에 의해서 Like를 받기 때문에 M:N 관계를 저용하면 됩니다.

이 때는 누가 M이고 누가 N인지는 별로 중요하지 않습니다.

2-2. 중간관리자 테이블의 필요성

이전에 했던 대로 Post 테이블에 Like_people이라는 컬럼을 새로 추가한다고 생각해봅시다.

Content Image user_id Like_people
첫글 /media/kermit1.jpg 1 2,3,4,5
첫글 /media/kermit2.jpg 2 3,4
두번째글 /media/cat1.jpg 1 5,4,2
자기소개 /media/self1.jpg 3 2,3,6

좋아요를 누른 사람이 여러명이므로, Like_people이라는 array를 저장해야하는데,

이건 매우 비효율적입니다. 만약 유저가 1억명이라면 각 포스트마다 1억명의 유저가 들어갈 수 있는 array를 만들어야 하기 때문입니다.

그래서 Many To Many relationship을 위해서는 추가로 하나의 테이블이 더 필요합니다.

2-3. 중간관리자 테이블

바로, Post와 User의 인덱스만 연결해주는 테이블을 만들어 줘야 합니다.

id post_id user_id
1 1 2
2 1 3
3 1 4
4 1 5
5 2 3
6 2 4
7 3 5

이런식으로 인덱스들을 쭉 가지고 있는 테이블들을 만들어줘야하는데,

이렇게 만들어주면, 1번 포스트에 좋아요를 누른 유저들을 쭉 검색할 수 있습니다.

뭔가 구조만 보면 약간 비효율적일 것 같지만,

Binary Search Tree같은 자료구조를 이용해 post_id 기준으로 정렬하고, 이진탐색을 통해서 주어진 post_id를 찾는 방식으로 구현하면 약간 괜찮을 것 같기도 합니다..

아무튼 쟝고에서도 좋은 알고리즘을 사용해서 M:N 관계를 잘 구현할 수 있습니다.

2-4. Django를 활용하기

Django에서는 ManyToManyField를 통해서 이러한 기능을 지원하는데요,

굳이 중간관리자 테이블을 만들지 않아도 됩니다. 쟝고에서 알아서 만들어서 관리하기 때문입니다.

(그리고 중복된 값을 허용하지 않는다는 장점도 있음!)

  1. Definition

    ManyToManyField( to = 상대 테이블, related_name = 상대 테이블에서 지칭할 이름)

    • to : User 클래스 => get_user_model()

    • related_name

      Django api reference를 참고했습니다.

      The name to use for the relation from the related object back to this one. It’s also the default value for related_query_name (the name to use for the reverse filter name from the target model). See the related objects documentation for a full explanation and example. Note that you must set this value when defining relations on abstract models; and when you do so some special syntax is available.

      그러니까 상대 테이블에서 related object를 부르기 위한 이름이라고 생각하시면 됩니다.

      (예시는 밑에..)

  2. Models.py

    class Post(models.Model):
        content = models.CharField(max_length=100)
        image = models.ImageField(blank=True)
        user = models.ForeignKey(get_user_model(), on_delete=models.CASCADE)
        ### 이게 추가됨 ###
        liked_users = models.ManyToManyField(get_user_model(), related_name="like_posts")
    
    • 두 테이블을 연결하는 방법

      post : 특정 포스트 객체

      user : 특정 유저 객체

      1. post.liked_users.all() : 이 포스트를 좋아요 누른 유저들의 집합
      2. user.like_posts.all() : 이 유저가 좋아요 누른 포스트들의 집합

2-5 그 외 메서드

  1. post.liked_users.add(유저 객체) : 추가하기
  2. post.liked_users.remove(유저 객체) : 제거하기

예시)


@login_required
def like(request, post_id):
    post = get_object_or_404(Post, id=post_id)
    u = request.user
    if post in u.like_posts.all():
        u.like_posts.remove(post)
    else:
        u.like_posts.add(post)

    return redirect('posts:list')
comments powered by Disqus