<실습>
- 좋아요 models-serializer-views
일단 좋아요를 모델에 추가해줘야 한다
좋아요는 유저와 게시글 사이에서 다대다 관계를 맺는다
한 유저가 여러 게시글에 좋아요를 누를 수 있고, 한 게시글에 여러 유저들이 좋아요를 누를 수 있다
# articles/models.py
...
class Article(models.Model):
...
likes = models.ManyToManyField(User, related_name="like_articles")
def __str__(self):
...
...
Article 모델에 likes 필드를 생성했다 User 모델과 다대다 관계를 가지고 있고, related_name은 like_articles로 했다 유저가 좋아한 게시글을 찾을 때는 user.like_articles로 찾을 수 있다
makemigrations, migrate 잊지 않고 해준 다음 admin 페이지로 가서 좋아요를 눌러본다
저기 선택된 사람들이 이 게시글의 좋아요를 누른 사람들이다 좋아요 기능은 잘 작동하는 것 같다
이제 views.py에서 좋아요 클릭 함수를 작성하자
# articles/views.py
...
class LikeView(APIView):
def post(self, request, article_id):
"""좋아요 클릭"""
article = get_object_or_404(Article, id=article_id)
if request.user in article.likes.all():
article.likes.remove(request.user)
return Response("좋아요 취소..", status=status.HTTP_200_OK)
else:
article.likes.add(request.user)
return Response("좋아요 완료!", status=status.HTTP_200_OK)
article은 댓글 조회했던 것처럼 불러오면 된다
article.likes.all()은 해당 게시글에 좋아요를 누른 유저 모두를 부른 것이고, 방금 클릭한 유저가 그 유저들 중에 있는지를 묻는 게 조건문이다
만약 있다면 이미 좋아요를 누른 것이니 remove 함수를 사용해서 해당 유저를 likes 목록에서 삭제한다
없다면 add함수를 사용해서 좋아요 목록에 해당 유저를 추가한다
이제 포스트맨 가서 실행을 해보자
like click 리퀘스트를 만들고 헤더에 로그인한 유저를 실어서 send하면 잘 작동되는 것을 확인할 수 있다
admin 페이지에서 해당 게시글에 좋아요를 누른 유저이기 때문에 좋아요가 취소되었다
다시 한 번 누르면 좋아요 상태로 변한다
좋아요 했을 때
좋아요 취소했을 때
- follow models-serializer-views
좋아요 기능이랑 로직이 비슷하다
먼저 User 모델에 followings 필드를 생성한다
# users/models.py
...
class User(AbstractBaseUser):
...
followings = models.ManyToManyField(
"self", symmetrical=False, related_name="followers")
...
follow 기능은 유저끼리 다대다 관계다 그래서 self로 User 모델 자신을 참조한다
symmetrical이 True이면 한쪽이 팔로우 했을 때 자동으로 맞팔이 된다 예전에 싸이월드 생각하면 된다 서로 팔로우하는 관계가 된다
False면 내가 상대를 팔로우 하면 나만 팔로우 할 뿐 상대가 내 팔로워가 되지는 않는다
related_name은 상대가 내 followings를 역참조 할 때 상대한테는 내가 follower가 되기 때문에 followers로 정했다
모델을 변경했으니 makemigrations, migrate를 잊지 않고 해준다
users앱의 urls.py에 팔로우 url을 만들어준다
# users/urls.py
...
urlpatterns = [
...
path('follow/<int:user_id>/', views.FollowView.as_view(), name='follow_view'),
]
이제 views.py에 가서 FollowView 함수를 작성해준다
# users/views.py
from rest_framework.generics import get_object_or_404
from users.models import User
...
...
class FollowView(APIView):
def post(self, request, user_id):
"""팔로우 기능"""
you = get_object_or_404(User, id=user_id)
me = request.user
if me in you.followers.all():
you.followers.remove(me)
return Response("팔로우 취소..", status=status.HTTP_200_OK)
else:
you.followers.add(me)
return Response("팔로우 완료!", status=status.HTTP_200_OK)
user가 2명이기 때문에 user로 하면 헷갈려서 로그인한 사용자는 me, 로그인한 사용자가 팔로우 하고 싶은 user는 you로 정했다
로직은 좋아요와 같다 you의 팔로우 목록에 me가 없으면 me를 추가해주고, 있으면 me를 삭제해준다
포스트맨에서 실행해보자
following 리퀘스트를 생성하고 url에 팔로우 하고 싶은 user의 아이디를 넣고 Header에 로그인 사용자 정보를 실어 send했더니 팔로우 완료! 라고 떴다
데이터베이스에 가보면 팔로잉 테이블에 데이터가 추가된 것을 볼 수 있다
다시 한 번 클릭하면 팔로우가 취소되고 데이터베이스에도 사라진 것을 확인할 수 있다
팔로우 취소
DB에 데이터가 사라짐
- 게시글 리스트 / 게시글 상세페이지 serializer 수정
게시글 상세페이지에 댓글들 나오게 하기
먼저 Comment 모델에 article 필드의 related_name을 설정해준다
Comment에서 article 필드를 Article 모델로 외래키를 사용하고 있어서 원래 기본값은 comment_set인데 게시글에서 댓글을 역참조할 때 게시글에 여러 댓글들이 있으니까 comments로 필드명을 정하는 것이 직관적이기 때문에 수정한다
# articles/models.py
...
class Comment(models.Model):
...
article = models.ForeignKey(
Article, on_delete=models.CASCADE, related_name="comments")
모델 수정했으니 makemigrations, migrate 해주기!!
이제 serializers.py로 이동한다
게시글 상세보기에서 참조하고 있는 건 ArticleSerializer니까 그 부분을 수정해준다
# articles/serializers.py
...
class ArticleSerializer(serializers.ModelSerializer):
...
comments = CommentSerializer(many=True)
...
이제 수정하고 싶은 부분은 해당 게시글에 달린 댓글들이기 때문에 댓글에 있는 article 필드는 필요없어서 빼고싶다
serializers.py에 가서 CommentSerializer를 변경해준다
# articles/serializers.py
...
class CommentSerializer(serializers.ModelSerializer):
class Meta:
model = Comment
exclude = ("article",) # 여기 변경!
원래는 fields = '__all__'였는데 모든 필드 중에 article 필드만 필요없어서 필드 중에 빼고 싶은 걸 뺄 수 있는 exclude를 사용했다(인스턴스인가..?모르겠다)
포스트맨에서 다시 실행해보면 article 필드가 사라진 것을 볼 수 있다
# articles/serializers.py
...
class ArticleSerializer(serializers.ModelSerializer):
...
likes = serializers.StringRelatedField(many=True)
...
class Meta:
...
이렇게 쓰고 저장한 후 포스트맨에서 실행해보면 like 필드의 user들이 메일형식으로 나오는 걸 볼 수 있다
흠 다시 보니 댓글 단 user도 메일형식으로 나오면 좋을 것 같아서 CommentSerializer를 수정했다
# articles/serializers.py
...
class CommentSerializer(serializers.ModelSerializer):
user = serializers.SerializerMethodField()
def get_user(self, obj):
return obj.user.email
class Meta:
model = Comment
exclude = ("article",)
이제까지 user 필드에 id로 나오는 걸 email로 나오게끔 바꾸는 코드를 그대로 복사했다
포스트맨에서 실행하면 댓글 작성자도 메일형식으로 나오는 걸 확인할 수 있다
게시글 리스트에 likes_count, comments_count 추가하기
전체 게시글 보기 함수에서 참조하는 시리얼라이저는 ArticleListSerializer니까 이 부분을 수정하면 된다
user가 id로 나오는 걸 email로 바꾼 것처럼 시리얼라이저메소드필드를 쓰면 된다
likes_count와 comments_count를 한 번에 만들어야겠다
# articles/serializers.py
...
class ArticleListSerializer(serializers.ModelSerializer):
...
likes_count = serializers.SerializerMethodField()
comments_count = serializers.SerializerMethodField()
...
def get_likes_count(self, obj):
return obj.likes.count()
def get_comments_count(self, obj):
return obj.comments.count()
class Meta:
model = Article
fields = ("pk", "title", "image", "updated_at",
"user", "likes_count", "comments_count",)
fields에 방금 만든 필드를 추가하는 것을 잊지 말아야 한다
시리얼메소드필드를 사용하면 모델에 해당 필드가 없어도 시리얼라이저를 통해 필드를 만들어줄 수 있다!