1) Posts App
1-1. OverView
-
Post 모델 작성
-
1:N relation
-
다른 유저가 Post에 ‘좋아요’ 기능을 넣어주고 싶음.
=> M:N relation
-
-
M:N relation
-
중간관리자 테이블의 필요성
- ManyToManyField
- 두 테이블을 연결하는 방법
-
1-2. Post와 유저를 연결
-
유저들의 정보를 담는 데이터베이스 : User 클래스, get_user_model()
-
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를 통해서 이러한 기능을 지원하는데요,
굳이 중간관리자 테이블을 만들지 않아도 됩니다. 쟝고에서 알아서 만들어서 관리하기 때문입니다.
(그리고 중복된 값을 허용하지 않는다는 장점도 있음!)
-
Definition
ManyToManyField( to = 상대 테이블, related_name = 상대 테이블에서 지칭할 이름)
-
to : User 클래스 => get_user_model()
-
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를 부르기 위한 이름이라고 생각하시면 됩니다.
(예시는 밑에..)
-
-
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 : 특정 유저 객체
- post.liked_users.all() : 이 포스트를 좋아요 누른 유저들의 집합
- user.like_posts.all() : 이 유저가 좋아요 누른 포스트들의 집합
-
2-5 그 외 메서드
- post.liked_users.add(유저 객체) : 추가하기
- 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')