일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | |||||
3 | 4 | 5 | 6 | 7 | 8 | 9 |
10 | 11 | 12 | 13 | 14 | 15 | 16 |
17 | 18 | 19 | 20 | 21 | 22 | 23 |
24 | 25 | 26 | 27 | 28 | 29 | 30 |
- QuerySet
- docker
- **kwargs
- clone coding
- promise
- 자바스크립트
- 자료구조
- 윈도우우분투듀얼부팅
- 파이썬
- Python
- 해시충돌
- 리스트컴프리헨션
- 알고리즘
- decorator
- wecode
- bcrypt
- 인증인가
- 파이썬리스트컴프리헨션
- 코딩테스트파이썬
- django
- 파이썬문법
- *args
- 백준
- JavaScript
- clone-coding
- 파이썬입출력
- 인터넷 네트워크
- CSS
- RESTfulAPI
- DP
- Today
- Total
개발기록장
[Django] 인스타그램 클론 코딩(6) - 팔로우(follow) 기능 구현 본문
[이전 글]
[Django] 인스타그램 클론 코딩(5) - 좋아요(Like) 기능 구현
[다음 글]
[Django] 인스타그램 클론 코딩(7) - 대댓글 기능 구현
다른 계정을 follow 하는 기능은 'Like' 기능을 구현한 것과 비슷하게 M:N 관계이다.
다만 다른 점이 있다면, User 테이블과 User 테이블이 M:N인 관계, 즉 셀프 참조로 M:N인 형태가 되어야 한다.
Django에는 셀프 참조하는 경우에 아래 예시처럼 ManyToManyField에 상대 테이블 이름 대신 'self'라고 적어서 셀프 참조를 구현할 수 있다.
예)
class User(models.Model):
(생략..)
relations = models.ManyToManyField('self', through= (생략...) )
(생략..)
하지만 follow 기능 구현에는 이 방법이 더 복잡한 것 같아서 이 방법을 이용하지 않고, 그냥 class를 통해 중간 테이블을 만들어서 ForeignKey로 관계를 설정하였다. 나도 공부하는 입장이라 이게 괜찮은 코드인건지 모르겠다....
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'
class Follow(models.Model):
from_user = models.ForeignKey('User', on_delete=models.CASCADE, related_name='to_user')
to_user = models.ForeignKey('User', on_delete=models.CASCADE, related_name='from_user')
class Meta:
db_table = 'follow'
-
'follow' 기능은 user와 관련이 있으므로 user app에 구현하였다.
-
Follow class는 User와 User 사이의 중간 테이블로 from_user, to_user 두 attribute 모두 User class를 ForeignKey를 갖는다.
-
이때 related_name 옵션이 반드시 설정되어 있어야 한다.(없으면 makemigration도 안됨)
** 셀프 참조 관계에서 related_name이 필수인 이유는 무엇일까?
아래 테스트를 통해 알아보자
In : user = User.objects.get(id=1)
In : user
Out: <User: User object (1)>
In : follow = Follow.objects.filter(from_user=user)
In : follow
Out: <QuerySet [<Follow: Follow object (2)>, <Follow: Follow object (5)>]>
In : follow.user
-
여기서 셀프참조가 아니라면 이 follow.user_set는 역참조(또는 정참조)로 해당 객체를 가지고 있어야 한다.
-
하지만 이 경우에는 user_set이라는 속성만으로는 자신을 바라보고 있는 두 User 객체 가운데 어떤 속성에 접근해야 할지 알 수가 없는 상황이 발생한다.
-
그래서 셀프 참조의 경우에는 반드시 related_name을 설정해야 한다.
2. user/views.py
import json
from django.views import View
from django.http import JsonResponse
from .models import User, Follow
from .utils import login_decorator
(생략..)
class FollowView(View):
@login_decorator
def post(self, request):
data = json.loads(request.body)
follow_user_id = data.get('follow_user', None)
# KEY_ERROR check
if not follow_user_id:
return JsonResponse({'message': 'KEY_ERROR'}, status=400)
from_user = request.user
to_user = User.objects.get(id=follow_user_id)
follow = Follow.objects.filter(from_user=from_user, to_user=to_user)
if follow:
follow[0].delete()
message = 'Unfollowing'
else:
Follow.objects.create(
from_user = from_user,
to_user = to_user,
)
message = 'Following'
return JsonResponse({'message': message}, status=200)
-
follow 기능도 로그인이 필수인 기능이므로 login_decorator를 통해 로그인 체크를 한다.
-
from_user는 follow를 요청한 사람을 의미하는데, 이 view는 나를 기준으로 중간 테이블의 데이터를 추가/삭제하는 것이기 때문에 from_user는 지금 접속한 user. 쉽게 말해 '나'를 의미한다.
-
to_user는 follow를 당한 사람. 이 view에서는 내가 follow를 건 사람을 의미한다. follow_user_id는 request body로 전달된다고 가정하고 코드를 작성하였다.
-
'Like' 기능과 마찬가지로 이미 follow를 하고 있는 다른 계정에 또 한 번 follow 버튼을 누른다면 unfollow 되어야 한다. unfollow가 된 경우에는 중간 테이블에서 from_user가 '나'이고 to_user가 내가 언팔한 계정인 데이터를 삭제한다.
-
내가 아직 follow 하고 있지 않은 상태라면 중간 테이블에 from_user, to_user를 매치시켜서 데이터를 insert 한다.
+ 추가 구현 - 특정 계정의 following user name, followers user name 가져오기
ForeignKey로 구현한 이 상태에서 related_name을 이용하여 특정 계정의 following user 정보와 follower user 정보를 조회해오는 것을 연습 삼아서 구현해보았다.
user/views.py
(생략..)
class FollowingView(View):
def get(self, request, user_name):
# valid user check
if not User.objects.filter(name=user_name).exists():
return JsonResponse({"message": "USER_DOES_NOT_EXIST"}, status=404)
user = User.objects.get(name=user_name)
followings = Follow.objects.filter(from_user=user)
if not followings:
return JsonResponse({'message': 'follow 하고 있는 계정이 없습니다.'}, status=204)
following_list = [following.to_user.name for following in followings]
return JsonResponse({'data': following_list}, status=200)
class FollowerView(View):
def get(self, request, user_name):
# valid user check
if not User.objects.filter(name=user_name).exists():
return JsonResponse({"message": "USER_DOES_NOT_EXIST"}, status=404)
user = User.objects.get(name=user_name)
followers = Follow.objects.filter(to_user=user)
if not followers:
return JsonResponse({'message': 'follower가 없습니다.'}, status=204)
follower_list = [follower.from_user.name for follower in followers]
return JsonResponse({'data': follower_list}, status=200)
3. user/urls.py
follow 기능과 following 유저, follower를 가져오는 기능의 엔드포인트 추가
from django.urls import path
from .views import (
(생략..)
FollowView,
FollowingView,
FollowerView,
)
urlpatterns = [
(생략..)
path('/follow', FollowView.as_view()),
path('/<str:user_name>/following', FollowingView.as_view()),
path('/<str:user_name>/follower', FollowerView.as_view()),
]
** url 경로를 설정하면서 follow에 관련된 기능이 추가가 된다면 이것과 관련된 기능은 user app에서 분리시켜서 따로 app을 만드는 게 나을 것 같다는 생각이 들었다.
'TIL > Django' 카테고리의 다른 글
[Django] Q객체를 이용한 ORM 조건절 (2) | 2021.02.28 |
---|---|
[Django] 인스타그램 클론 코딩(7) - 대댓글 기능 구현 (0) | 2021.02.10 |
[Django] 인스타그램 클론 코딩(5) - 좋아요(Like) 기능 구현 (0) | 2021.02.09 |
[Django] 인스타그램 클론 코딩(4) - 게시물과 댓글 C.R.U.D (RESTful API) (0) | 2021.02.09 |
[Django] 인스타그램 클론 코딩(3) - Authorization Decorator 만들고 활용하기 (1) | 2021.02.08 |