[drf]airbnb-api -12 Magic + FavsView

Hyeseong·2021년 3월 26일
0
post-thumbnail

아래를 보면 그래서 하나는 작성용! 읽기용! 이렇게 구분해서 시리얼라이저를 작성한거에요.

하지만 이렇게 2번 나눠서 작성하는 것 역시 파이써닉한 방법이 아니고 중복된 코드를 발생시키는 부분입니다.

변경 전

변경 후

하나로 통합하고 이름도 더 포괄적으로 룸시리얼라이저로 개명했습니다. 여기서 눈여겨 볼만한 메타클래스의 변수중 하나가 바로 read_only_fields 딱! 봐도 느낌오조?!

nested Serializer에서 가져온 필드중 4개를 딱! 읽기만 하겠다는 의미에요.

create, update를 하는 경우 자동으로 validate를 하게 될때 user만 하지 말라고 제약을 걸어주는거조.

참고로 read_only_fields는 Serialzer클래스에는 없고 ModelSerializer에만 있어요.

from .models import Room


class RoomSerializer(serializers.ModelSerializer):

    user = RelatedUserSerializer()

    class Meta:
        model = Room
        exclude = ("modified",)
        read_only_fields = ("user", "id", "created", "updated")

    def validate(self, data):
        if self.instance:

/me/fav

ussers/urls.py


#... omitted

urlpatterns = [
    path("me/", views.MeView.as_view()),
    path("me/favs/", views.toggle_fav),
    path("<int:pk>/", views.user_detail),
]

users/views.py

from rest_framework.decorators import api_view, permission_classes
...
... 

# FBV로 작성
@api_view(['POST', 'GET'])
@permission_classes([IsAuthenticated])
def toggle_fav(request):
room = request.data.get('room')
print(room)

여기서 발생하는 문제가 있어요. 바로 user/me에서는 통상적으로 본인이 favrite한 방들이 보여야해요. 혹은
1000개나 보여주면 안되기 때문에 보여줄 필요가 없을수도 있조.

그렇다면 결국 profile에는 fav가 보이지 않게 하는게 더 확장성이나 일관성에 있어서 좋아보이겠조?!

exclude에 fav를 추가하도록 할게요.

그렇다면 url디자인을 다시 한번 점검해보면

users/me/favs/에서는 좋아하는 방들이 열거되어야하겠조?

APIView로 갈아타기

그래서 위에서 언급한 결과 종합적으로 갈아 타야 하는거조.

잠깐만요 근데 왜? post가 아니고 put일까요?
favorite이 뭔가를 실제 만드는건가요? DB에 새로운 걸 만든다면 맞겠지만 이미 있는걸 상태 변화만 하기에 put에 더 적합해요.

class FavsView(APIView):

    permission_classes = [IsAuthenticated]

    def get(self, request):
        user = request.user
        serializer = RoomSerializer(user.favs.all(), many=True).data
        return Response(serializer)

    def put(self, request):
        pass

물론 url도 as_view() 메서드를 사용할 수 있게 클래스로 만들어야겟네요.

urlpatterns = [
    path("me/", views.MeView.as_view()),
    path("me/favs/", views.FavsView.as_view()),
    path("<int:pk>/", views.user_detail),
]

관리자 페이지

users/admin.py

왜? 관리자 페이지에서 이러고 있냐고요?

아직 JWT 토큰을 안 만들어서 우선적으로 관리자 페이지에서 작업을 통해서 해결하는거조.

from django.contrib import admin
from django.contrib.auth.admin import UserAdmin
from . import models
@admin.register(models.User)
class UserAdmin(UserAdmin):

    fieldsets = UserAdmin.fieldsets + (
     ("Custom Profile", {"fields": ("avatar", "superhost", "favs")},),
    )

    list_display = UserAdmin.list_display + ("room_count",)

어쨋든 다시 화면으로 돌아가면

한 유저가 좋아하는 방과 관계를 맺어 주었어요.
여기서 관계를 맺어주었다는 표현에 주목해주세요.

참고로 다대다 관계이기에 이런 표현을 사용하는 거에요.

browsable API에서 한번 확인해볼게요.
로그인한 계정의 좋아하는 방의 목록이 나오고 있네요.

users API

if room in user.favs.all():에서 in 멤버십 연산자를 처음 왜? 사용했지? 라고 생각할 수 있어요.

왼쪽의 room은 쿼리 파라미터로 주소창에서 방의 pk를 가져온거에요.그래서 인스턴스로 가져와서 결국 현재 user가 좋아하는 favs에 있는지 없는지! 포함 관계인지 아닌지 확인하기 위해서입니다.

from rest_framework.response import Response
from rest_framework.permissions import IsAuthenticated
from rest_framework.decorators import api_view
from rest_framework.views import APIView
from rest_framework import status
from rooms.serializers import RoomSerializer
from rooms.models import Room
from .models import User
from .serializers import ReadUserSerializer, WriteUserSerializer

...
...
...
   
    def put(self, request): # user의 favs필드의 상태를 업데이트 하기에 post가 아님  
        pk = request.data.get("pk", None)
        user = request.user
        if pk is not None:
            try:
                room = Room.objects.filter(pk=pk)
		if room.exists():
                    user.favs.remove(room) # orm을 통해서 객체간의 연결을 해체
                else:
                    user.favs.add(room) # 1:N 관계를 orm을 통해서 연결함
                return Response() # 생략시 200 status code 기본값으로 반환하여줌
            except Room.DoesNotExist:
                pass
        return Response(status=status.HTTP_400_BAD_REQUEST)

데모

기존 150이 있네요.
여기서 PUT 요청을 하면 리스트에서 사라져야해요.

보다시피 사라져버렸조?!

느낀점

: RESTful 하게 API를 디자인하는 것에 대한 어려움을 느꼇고. 어찌보면 FBV를 사용함에 있어 직관성을 극대화 시킬수 있지만 이런 저런 헷갈리는 상황에서는 오히려 시간과 노력을 허비하는 느낌을 많이 받을 수 있다.

이럴때 CBV를 사용하면 좋은건가? 생각해보게 된다.
FBV -> CBV | Serializer -> ModelSerializer -> APIView, ListAPIView 등등 -> Mixin
결국 끝판왕?인 ViewSet을 통해서 router를 쓰게 되겠지만 지금은 탄탄히 하나하나 짚고 넘어가야하는 단계인것 같다.

profile
어제보다 오늘 그리고 오늘 보다 내일...

0개의 댓글