Project2 - Message & Follow function

Heechul Yoon·2020년 3월 20일
0

LOG

목록 보기
34/62

이번에는 2차프로젝트 기간에 만들었던 notification기능, follow status기능과 message기능을 리뷰해보겠다.

Notification and Follow Status


로그인을 하고 나서 나에게 누군가 나를 팔로우하면 위와같이 notification에 빨간불이 들어온다.(메세지도 새로운 메세지가 오면 메세지 창에 빨간불이들어옴.)

class NotificationView(View):
    @login_required
    def get(self, request):
        user = request.user.id
[0]     latest_message = Message.objects.filter(to_user_id = user).order_by('created_at').last()
        latest_follower = Follow.objects.filter(to_follow_id = user).order_by('created_at').last()
        
[1]     if not message_checked  or not follow_checked:
            return_key = {
                    'data' : {
                        'message_checked' : latest_message.is_checked,
                        'follow_checked'  : latest_follower.is_checked,
                        'follower_id'     : latest_follower.from_follow_id,
                        'sender'          : latest_message.from_user_id,
                    }
            }
            return JsonResponse(return_key, status = 200)
        
[2]     return_key = {
                'data' : {
                    'message_checked' : message_checked, 
                    'follow_checked'  : follow_checked,
                }
        }
        return JsonResponse(return_key, status = 200)

코드를 보면
[0] 최근에 생성된 나에게 보내진 메세지와 팔로우 객체를 가져오기 위해 생성순서대로 객체들을 정렬해주고 last()를 사용해서 가장 최근에 생성된 객체를 가져온다.
[1] is_checked 가 False(0)일때, 가장 최근에 생성된 메세지와 팔로우 객체의 is_checked값을 가져온다. 생성되었을 때 default로 False가 들어가기 때문에 체크가되지 않은 객체는 False(0)가 온다. 그리고 체크여부와 유저정보를 리턴한다.
[2] is_checked가 True(1)일때는 메세지와 팔로우의 체크 여부만 리턴하도록 한다.

notifications을 클릭하면

위와같이 누가 나를 팔로우했는지, 그리고 나도 그사람을 팔로우한 상태인지(mutual follow, 맞팔) 알 수 있다.

class StatusView(View):
    @login_required
    def get(self, request):
        user = request.user.id
[1]     follow_status  = (
                Follow.objects
                .filter(to_follow_id = user, is_checked = False)
                .select_related('from_follow', 'to_follow')
                .order_by('created_at')
        )
[2]     if not len(list(follow_status)):
            return JsonResponse({'message' : 'EMPTY_UPDATES'}, status = 400)

[3]     return_key = {
            'data' :
                [{'follower_name'             : status.from_follow.name,
                    'follower_id'             : status.from_follow.id,
[4]                 'follower_follower_count' : status.from_follow.follow_reverse.all().count(),
                    'follower_song_count'     : status.from_follow.song_set.all().count(),
                    'follower_image'          : status.from_follow.profile_image,
                    'follow_at'               : status.created_at,
                    'is_checked'              : status.is_checked,
[5]                 'mutual_follow'           : True if Follow.objects.filter(from_follow_id = user, to_follow_id = status.from_follow_id) else False}
                    for status in follow_status]
        }
[6]     Follow.objects.filter(to_follow_id = user).update(is_checked = True)

        return JsonResponse(return_key, status=200)

[1] 팔로우 대상이 user(토큰을 가지고있는 유저)이고, 체크가 되어있지 않은 팔로우객체를 생성순서대로 정렬해서 가져온다.
[2] 가져온 값이 없으면 업데이트 된 상태가 없기 때문에 empty update를 리턴한다.
[3] 값이 있으면 나를 팔로우한 상대방의 정보를 가져온다(from_follow_id에 해당)
[4] 나를 팔로우하는 상대방의 팔로워가 몇명인지 가져온다. 모델에서 설정해주었던 related_name인 follow_reverse를 통해서 값을 가져올 수 있다.
[5] 삼항연산자를 사용해서 내가 나를 팔로우한 상대방을 팔로우하고 있으면 True, 아니면 False를 값을 리턴하는 객체를 만든다.
[6] 나를 팔로우한 상대방의 정보를 end-point호출로 확인 했기 때문에 is_checked를 True로 바꿔준다.

소켓통신으로 상대방이 나를 팔로우하면 실시간으로 내 상태가 업데이트 되는 것이 아니기 때문에 프론트앤드와 협의해서 창을 옮길 때 마다, 또는 일정 시간마다 notification 앤드포인트 호출을 통해서 상태를 나의 팔로우 notification 상태를 업데이트해주도록 한다.

메세지

메제시 전체


위의 사진은 토큰을가진 유저가 상대방과 주고받은 대화를 하나의 chunk로 하여 묶여있다. 그래서 토큰을 가진 유저가 수신자가 된 경우와 발신자가 된 두가지 경우의 메세지 객체를 가져와서 중복값을 재거해주어야 한다. 중복값의 제거에 대해서는 여기를 참고해보자.

위의 사진에서 view all messages를 누르면 아래와 아래와같이 다른유저와 주고받은 매세지 묶음들을 볼 수 있다.


처음의 사진은 썸내일 개념으로 3개만 보여주고, 클릭하고 들어오면 전체를 보여주는 코드를 보자.

    @login_required
    def get(self, request):
        try:
            user          = request.user.id
            message_chunk = Message.objects.filter(Q(from_user_id = user)|Q(to_user_id = user)).order_by('created_at')
            offset        = int(request.GET.get('offset', 0))
            limit         = int(request.GET.get('limit', message_chunk.count()))

우선 offset과 limit값을 쿼리파라미터로 받아온다. offset(시작값)은 이 경우 0일 경우가 대부분일 것이기 때문에 default를 0으로 준다. limit(보여주는 갯수)값은 값이 들어오지 않았을 경우를 대비해 위에서 정의해준 message_chunk의 갯수만큼을 default로 지정한다.

return JsonResponse({'data' : messages_to_user[offset:offset + limit]}, status = 200)

그리고 마지막에 리턴값에 들어온 offset과 limit대로 slicing을 걸어주어 pagination을 만든다. 만약 limit값이 들어오지 않은 경우 유저들과 나눈 메세지 chunk를 다 보여주고, limit값이 들어온 경우 limit값 만큼만 보여준다.

메세지 상세

  • GET

    사진과 같이 내가 상대방과 나눈 대화의 history를 볼 수 있는 곳이다.
    @login_required
    def get(self, request):
        try:
            user          = request.user.id
            message_chunk = Message.objects.filter(Q(from_user_id = user)|Q(to_user_id = user)).order_by('created_at')
            offset        = int(request.GET.get('offset', 0))
            limit         = int(request.GET.get('limit', message_chunk.count()))
[0]         to_user       = request.GET.get('to_user', None)


            if to_user:
[1]             message_details = (
                        Message.objects.prefetch_related('playlist', 'song')
                        .filter((Q(from_user_id=user)&Q(to_user_id=to_user))|(Q(from_user_id=to_user)&Q(to_user_id=user)))
                        .order_by('created_at')
                )
                messages = [
                        {
                    'message_id'     : message.id,
                    'content'        : message.content,
                    'from_user_id'   : message.from_user_id,
                    'from_user_name' : message.from_user.name,
                    'from_user_img'  : message.from_user.profile_image,
                    'to_user_id'     : message.to_user_id,
                    'to_user_name'   : message.to_user.name,
                    'to_user_img'    : message.to_user.profile_image,
                    'is_checked'     : message.is_checked,
                    'created_at'     : message.created_at,
                    'playlist'       : [{'playlist_id' : data.id, 'name' : data.name} for data in message.playlist.all()],
                    'song'           : [{'song_id' : data.id, 'name' : data.name} for data in message.song.all()],
                    }
                    for message in message_details
                ]
[2]             Message.objects.filter(to_user_id = user).update(is_checked = True)

                return JsonResponse({'message_details' : messages}, status = 200)

[0] 상대방의 id를 쿼리파라미터로 받는다.
[1] 수신인이 토큰을 가진 유저와 송신인이 쿼리파라미터로 받은 상대방이 주고받은경우, 그 반대의 경우의 메세지 객채를 가져온다.
[2] 메세지를 확인했으니깐 is_checked를 True로 바꿔놓는다.

is_checked가 True로 바뀌어져서 다음에 notifiaction 요청에 True로 잡혀서 알림창에 불이켜지지 않는다.

  • POST

    위의 메세지 내용을 입력하고 send를 누르면 내 메세지가 데이터베이스에 저장되고, 보내지는 즉시 내가 쓴 메세지를 볼 수 있어야한다. 그래서 send를 누르는 순간 프론트앤드에서는 POST로 input값을 데이터베이스에 보내고, GET요청을 해서 방금 내가 보내서 저장된 값을 불러와 화면에 보여준다.
class MessageView(View):
    @login_required
    def post(self, request):
        data     = json.loads(request.body)
[1]     playlist = data.get('playlist_id', None)
[2]     song     = data.get('song_id', None)
[3]     message  = Message.objects.create(
                content      = data['content'],
                from_user_id = request.user.id,
                to_user_id   = data['to_user_id'],
        )

[1]     if playlist:
            MessagePlaylist.objects.create(
[4]             message_id  = message.id,
                playlist_id = playlist,
            )

[2]     if song:
            MessageSong.objects.create(
                message_id = message.id,
                song_id    = song,
            )

        return HttpResponse(status = 200)

사운드 클라우드 메세지의 특성상 메세지로 내 플레이리스트와 내가만든 음악을 보낼 수 있다.
[1]과 [2]에서 처럼 song과 playlist는 값이 들어올 수도 안들어올 수도 있기 때문에 값이 들어오지 않을 경우 default로 None을 넣어서 조건문이 실행되지 않고 Message객체가 생성되도록 한다.

[3]에서와 같이 Message객체를 생성시키고 그 객체를 message라는 변수에 담아준다. 그리고 [4]와 같이 message와 playlist의 중간테이블에 foreignkey에 해당하는 message id값을 방금 변수화한 message객체의 id값에 대응시킨다.

profile
Quit talking, Begin doing

0개의 댓글