• 카테고리

    질문 & 답변
  • 세부 분야

    풀스택

  • 해결 여부

    해결됨

class기반 뷰로 작성

22.12.22 11:28 작성 조회수 224

0

안녕하세요 강사님 수업 열심히 따라가고 있습니다. class 기반뷰로 작성을 해보았는데, 이런식으로 작성하는 것이 맞는지 질문 드립니다.

기본 로직은 같고 APIView를 사용하였습니다.

class UserFollow(APIView):
    def post(self, request):
        username = request.data['username']
        follow_user = get_object_or_404(get_user_model(), username=username, is_active=True)
        request.user.following_set.add(follow_user)

        return Response(status.HTTP_204_NO_CONTENT)

답변 1

답변을 작성해보세요.

1

안녕하세요.

예. 맞습니다. 사용할 http method 이름에 맞춰, 메서드를 구현하시면 됩니다.

화이팅입니다. :-)

honge7694님의 프로필

honge7694

질문자

2023.01.16

안녕하세요 강사님!

generics view를 이용한 팔로우 기능을 개발해보고 있는데, MTM를 이용하여 만들어진 테이블 접근 하는 것이 궁금합니다.

get_queryset()을 이용하여 MTM으로 만들어진 테이블에 접근 후 쿼리 결과가 있으면 삭제, 없으면 생성을 하고싶은데, MTM으로 만들어진 테이블에 접근하는 방법을 모르겠어서 막혀있는 상태입니다.

혹시 models.py에서 Follow라는 테이블을 만들어 사용하여 접근하는 것이 더 좋은 것인지 아니면 MTM 테이블에 접근방법이 있는지 또는 다른 방법이 있는지 궁금해서 질문드립니다.

# models.py
class User(AbstractBaseUser):
    email = models.EmailField(max_length=255, unique=True)
    following_set = models.ManyToManyField('self', related_name='follower_set')
 
# serializers.py
 class FollowSerializer(serializers.Serializer):
    user = serializers.SerializerMethodField(read_only=True)

    class Meta:
        model = User
        fields = ['user', 'following_set']

    def get_user(self, obj):
        self.context['request'].user
    
# views.py
class FollowAPIView(ListCreateAPIView):
    serializer_class = FollowSerializer

    def get_queryset(self):
        user = self.request.user
        follower_user = get_user_model().objects.filter(following_set=user)
        return follower_user

    def perform_create(self, serializer, *args, **kwargs):
        if self.get_queryset().exists():
            self.get_queryset().delete()
            return Response(status=status.HTTP_204_NO_CONTENT)
        follower_user = get_user_model().objects.get(pk=self.kwargs['pk'])
        self.request.user.following_set.add(follower_user.id)

위 로직의 문제는 following하는 상황만이 아닌 follower까지 함께 생성되어 MTM테이블에 2개의 행이 생성되고, 삭제는 MTM테이블의 모든 행이 삭제가 됩니다..

강의의 "User 모델에 Follow-Unfollow 관계 필드를 구현하고, Follow 기능 구현" 수업에서 비디오 플레이어 아래의 보충 설명을 참고해보시겠어요?

https://www.inflearn.com/course/%ED%8C%8C%EC%9D%B4%EC%8D%AC-%EC%9E%A5%EA%B3%A0-%EC%9B%B9%EC%84%9C%EB%B9%84%EC%8A%A4/unit/90589

제가 강의에서는 symmetrical 설명을 누락되었었는데, 이에 대한 보충입니다.


M2M 에 대한 질문은 제가 질문이 잘 이해가 안 되어서요. 질문을 보충해서 좀 더 상세히 부탁드려도 될까요? // LIke 모델과 User Follower에는 어떤 관계가 있나요?

honge7694님의 프로필

honge7694

질문자

2023.01.16

아.. 코드를 잘 못 올렸었습니다.
2개씩 생성되고 삭제되는 것은 symmetrical를 이용하여 해결하였습니다! 감사합니다.

M2M에 대한 질문은 제가 Post.like 부분에서 M2M으로 작성하였는데 아래와 같이 했었습니다.

# models.py
class Post(models.Model):
    user = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE)
    title = models.CharField(max_length=30)
    content = models.TextField(blank=True)
    like = models.ManyToManyField(settings.AUTH_USER_MODEL, related_name='post_like_set', through='Like')
    created_at = models.DateTimeField(auto_now_add=True)
    updated_at = models.DateTimeField(auto_now=True)

    def __str__(self):
        return str(self.title)


class Like(models.Model):
    user = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE)
    post = models.ForeignKey(Post, on_delete=models.CASCADE, related_name='like_post_set')

# views.py
class LikeAPIView(ListCreateAPIView):
    serializer_class = LikeSerializer

    def get_queryset(self):
        user = self.request.user
        post = Post.objects.get(pk=self.kwargs['pk'])
        return Like.objects.filter(user=user, post=post)

    def perform_create(self, serializer, *args, **kwargs):
        if self.get_queryset().exists():
            self.get_queryset().delete()
            return Response(status=status.HTTP_204_NO_CONTENT)
        serializer.save(user=self.request.user, post=Post.objects.get(pk=self.kwargs['pk']))

through를 통해 Like 테이블에 접근할 수 있도록 하였는데 이렇게 작성하니 M2M을 제대로 사용하지 못하다 생각이 들었었습니다. 그래서 User.follow는 through를 사용하지 않고 views를 작성하다가 query_set을 어떻게 주어야할지 고민했었는데, 현재는 아래와 같이 작성하여 해결했습니다!!

**그런데, add할 때 status 코드가 정상적으로 201이 오는데 remove할때 status 코드 204를 줬는데도 201이 오는 이유가 있을까요??

class FollowAPIView(ListCreateAPIView):
    serializer_class = FollowSerializer

    def get_queryset(self):
        user = self.request.user
        follower_user = get_user_model().objects.filter(pk=self.kwargs['pk'], follower_set=user)
        return follower_user

    def perform_create(self, serializer, *args, **kwargs):
        follower_user = get_user_model().objects.filter(pk=self.kwargs['pk']).first()
        if self.get_queryset().exists():
            self.request.user.following_set.remove(follower_user.id)
            return Response(status=status.HTTP_204_NO_CONTENT)
        self.request.user.following_set.add(follower_user.id)

M2M 필드에서 .add 에서는 내부적으로 중복을 체크합니다. 여러번 .add 해도 처음 1회만 관계를 저장합니다. 그러니 굳이 삭제하지 않으시고, add 만 하셔도 충분합니다. // .exists, .remove 등을 하실 필요가 없는 거죠.

M2M 관계는 서로 복수 관계를 다룹니다. 그러니 아래와 같이 like 이름처럼 단수형으로 쓰지 마시고,
like = models.ManyToManyField(settings.AUTH_USER_MODEL, related_name='post_like_set', through='Like')

저라면 아래와 같이 쓰겠습니다.

liked_user_set = models.ManyToManyField(settings.AUTH_USER_MODEL, related_name='liked_post_set', through='Like')

그럼 post.liked_user_set.add(user) 와 같이 쓰시거나,
user.liked_post_set.add(post) 처럼 쓰실 수 있습니다.

204 응답이 오는 것은 204 루틴을 탔기 때문일 것입니다. 로직을 차근차근 확인해보시고, 디버거를 사용해보지 않으셨다면, VSCode 혹은 PyCharm 디버거를 물려서 코드를 한 줄씩 실행해보시며, 실행흐름을 파악해보셔도 좋습니다.

화이팅입니다. :-)