[drf]airbnb-api -20 UsersViewSet

Hyeseong·2021년 4월 1일
0
post-thumbnail

router 작업

기본 라우터를 연결시켜뒀어요.

비즈니스 로직 in views.py

RoomViewSet을 앞 시간에 만들었는데요. 이번에는 User APIView를 ViewSet으로 바꿀게요.
원리는 RoomViewSet과 동일해요.

from rest_framework.decorators import api_view
from rest_framework.views import APIView
from rest_framework import status
from rest_framework.viewsets import ModelViewSet # 추가
from rest_framework.permissions import IsAdminUser, AllowAny # 추가
from rooms.serializers import RoomSerializer
from rooms.models import Room
from .models import User
from .serializers import UserSerializer



class UsersViewSet(ModelViewSet):

    queryset = User.objects.all()
    serializer_class = UserSerializer

    def get_permissions(self):
        permission_classes = []
        if self.action == "list": # 유저목록을 볼수 있는 Endpoint를 접근권한을 오직 관리자만 부여함
            permission_classes = [IsAdminUser]
        elif self.action == "create" or self.action == "retrieve": # 누구나  회원 가입 가능한 API 그다음 누구나 단일 유저 정보에 대한 값을 조회해서 확인 가능
            permission_classes = [AllowAny] 

        return [permission() for permission in permission_classes]

수정, 삭제 권한 부여

컨테츠의 수정과 삭제 권한은 누구나 가지고 있으면 안디기에 request.user를 통해서 유저 정보를 확인하고 기존 db에서 room인스턴스의 생성자 정보와 비교하여 일치하면 수정과 삭제 기능을 활성화 시킬수 있는 로직인데요.

메서드 자체에서 냄새가 물씬나조. 디비에서 가져온 인스턴스의 정보와 request객체의 user속성과 일치하는지 판단하는 메서드에요.

커스터 마이징하여 사용하는 인증인가 클래스라서 나름 유요하게 사용할 수 있기에 숙지하세요.

users/permissions.py

from rest_framework.permissions import BasePermission


class IsSelf(BasePermission):
    def has_object_permission(self, request, view, user):
        return user == request.user

users/views.py

기존 작성했던 MeView APIView를 삭제하고 UserViewSet에 login메서드를 만들어서 ~/users/login엔드포인트를 만들게 하는거에요. 당연헤 http method는 post만 허용하도록 하고요. 그러기 위해선 action데코레이터의 2번쨰 인자에 post를 적어서 기본값으로 잡혀있는 get을 사용하지 못하게 하는 일석 이조의 효과도 만들어둡니다.

그리고 토큰 발행 로직을 작성하고 깜빡하지 말고 유저의 pk정보를 꼭 같이 넣어서 넘겨줘야해요.

...
...
from rest_framework.decorators import action
from .permissions import IsSelf
...


class UsersViewSet(ModelViewSet):
    serializer_class = UserSerializer

    def get_permissions(self):
        permission_classes = []
        if self.action == "list":
            permission_classes = [IsAdminUser]
        elif self.action == "create" or self.action == "retrieve":
            permission_classes = [AllowAny]
        else:
            permission_classes = [IsSelf | IsAdminUser] # 컨텐츠 생성자와 관리자가 수정 및 삭제가 가능하더로 인가를 부여했어요.
        return [permission() for permission in permission_classes]


    @action(detail=False, methods=["post"])
    def login(self, request):
        username = request.data.get("username")
        password = request.data.get("password")
        if not username or not password:
            return Response(status=status.HTTP_400_BAD_REQUEST)
        user = authenticate(username=username, password=password)
        if user is not None:
            encoded_jwt = jwt.encode(
                {"pk": user.pk}, settings.SECRET_KEY, algorithm="HS256"
            )
            return Response(data={"token": encoded_jwt, "id": user.pk})
        else:
            return Response(status=status.HTTP_401_UNAUTHORIZED)

favs endpoint구현

해당 유저가 좋아요한 목록 API도 바로 login 소스코드 아래에 만들어볼게요.
get_object()는 viewset을 통해서 인스턴스를 받아와 user변수에 할당해주게 되요. 이후 로직은 통상적인 drf의 get방식으로 단인 정보를 받아오게 되고요.

아래 toggle_favs메소드는 좋아하는 방에 대한 수정을 위한 로직이에요.
유의미하게 봐야 할점은 decorator로 매핍한 부분인데요. @'함수명'.mapping.'httpmethod이름' 이렇게 되요. 기능은 다르지만 분명 동일한 url이에요.

또한 접근 권한도 다르게 받아옵니다. GET메소드로 갔을 때는 누구나 favs를 확인할수 있지만 puth메서드로 맵핑된 경우에는 로그인된 유저만 가능하조.

	@action(detail=True)
    def favs(self, request, pk):
    user = get_object()
    serializer = RoomSerializer(user.favs.all(), many=True).data
    return Response(serializer)
    
    @favs.mapping.put
    def toggle_favs(self, request, pk):
        pk = request.data.get("pk", None)
        user = self.get_object()
        if pk is not None:
            try:
                room = Room.objects.get(pk=pk)
                if room in user.favs.all():
                    user.favs.remove(room)
                else:
                    user.favs.add(room)
                return Response()
            except Room.DoesNotExist:
                pass
        return Response(status=status.HTTP_400_BAD_REQUEST)
profile
어제보다 오늘 그리고 오늘 보다 내일...

0개의 댓글