[drf]airbnb-api -10 Room Detail DELETE PUT

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

room 하나에 대한 detail한 정보를 보여주기 위한 비즈니스 로직을 짜볼게요.

get_room helper funcition을 만들건데요. get, delete, put 메소드의 중복된 코드가 발생되지 않게 ORM의 get()메서드를 사용했을 경우의 로직을 별도로 빼버린거에요.

  • put() 메서드의 경우
    WriteRoomSerializer()클래스가 2가지 인자를 가지게 되요. 하나는 인스턴스, 나머지 하나는 클라이언트의 body에서 받아오는 data값이에요.
    인스턴스를 통해서 시리얼라이저는 초기화 작업을 처음 하게 됩니다.

WriteRoomSerializer가 create(), update()메서드를 구분하는 것은 결국 인스턴스를 인자로 받느냐?!(업데이트) 받지 않느냐(create)에 따라 달라지게되요.

Serializer클래스의 옵션

  • partial=True
    기본적으로 put요청시 drf에서는 모든 정보를 입력하여야 유효성 검사를 통과하게되요.
    하지만 파샬트루!를 해주게 되면 가격 또는 이름 등 전체중 부분만 바꾸어서 요청을 보내도 오류없이 정상적으로 로직 수행을 가능하게해요.

rooms/views.py

# 생략

class RoomView(APIView): # single로 처리
    def get_room(self, pk): # DB에 존재하면 긁어 오고 없으면 None반환, 일명 helper function
        try:
            room = Room.objects.get(pk=pk)
            return room
        except Room.DoesNotExist:
            return None    

    def get(self, request, pk):
        room = self.get_room(pk)
        if room is not None:# room 객체가 정상적으로 DB에 있을 경우
            serializer = ReadRoomSerializer(room).data
            return Response(serializer)
        else: # Room 객체가 None에 해당할 경우
            return Response(status=status.HTTP_404_NOT_FOUND)

    def put(self, request, pk):
        room = self.get_room(pk) # get_room()메서드 정의시 첫번째 인자로 self로 받아서 인스턴스 속성으로 사용 가능
        if room is not None:
            if room.user != request.user: # url  parameter로 받아와 db에서 긁어온게 왼쪽, request객체에서 가져온 유저의 정보가 오른쪽
                return Response(status=status.HTTP_403_FORBIDEN)
            serializer = WriteRoomSerializer(room, data=request.data, partial=True) # partial 사용해서 기본적으로 required fields를 모두 넣지 않아도 오류를 발생 시키지 않게함
            if serializer.is_valid(): # db에 가져온 객체 정보와 클라이언트로부터 가져온 정보를 serializer와 비교했을때 유효한가?
                serializer.save()     # update() 메서드를 호출함.  객체 생성후 db에 저장함
            else:
                return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
            return Response()
        else: # pk로 뒤져본 room이 디비에 없어요.
            return Response(status=status.HTTP_404_NOT_FOUND)

    def delete(self, request, pk): # 3번째 인자는 urls.py에서 url 파라미터로 넘겨 받은 변수
        room = self.get_room(pk) # 헬퍼 함수를 통해서 room이 db에 있는지 혹은 None인지 반환
        if room is not None:     # 객체가 존재하고
            if room.user != request.user:  # 로그인한 유저가  객체의 user가 아니면(만든사람)
                return Response(status=status.HTTP_403_FORBIDDEN) # 403 오류를 뱉어내고
            room.delete()   # room.user == request.user 동일하면 delete()메소드를 사용해서 db에서 지워버림
            return Response(status=status.HTTP_200_OK) # 정상 삭제되었다는 200 코드를 반환함
        else:
            return Response(status=status.HTTP_404_NOT_FOUND) # pk를 이용해서 조회했지만 없는 경우 404오류를 Response클래스에 담아서 반환함. 

edit room - update()

update()메서드를 WriteRoomSerializer클래스에 작성할게요.
눈에 띄는 점이 두번째 인자가 instance라고 되어있는데요.

instance를 통해서 django restframework가 get or update를 진행하는지 알게되는거에요.
instance는 db에서 가져온 기존 정보를 말해요.

또한 현재 validate의 로직은 오직 POST요청에만 정상적으로 작동하고 PUT요청에는 오류를 발생시키는 구조로 되어있어요.
이부분은 추후 수정해보도록하조.

PUT요청시 통상 check_in, check_out 키-벨류 값을 같이 넣어주지 않으면 반드시 오류 날거에요. 안넣으면 None값을 각각 변수로 받아 처리 하기 때문이니깐요. 그래서 if not self.instance: 문을 넣어서 처리함으로써 create, update로직을 유연하게 대처할 수 있게되요.

1단계

rooms/serializers.py

# 생략

    def validate(self, data): 
        if not self.instance: # create() 메소드를 통해서 self.instance가 정확히 값을 None이 아닌 값을 참고 하고 있을때 작동하게 만듬
            check_in = data.get('check_in')
            check_out = data.get('check_out')
            if check_in == check_out:
                raise serializers.ValidationError('Not enough time between changes')
        return data
        
   def update(self, instance, validated_data):
        print(instance, validated_data) 

2단계

  • post, put 메서드 요청시 해당 DB에서 인스턴스 유무에 따라 로직 양상이 달라짐.

    • self.instance가 있다면, raw data를 get()로 키값을 조회하고 default값을 인스턴스로 두게됨
    • self.instance가 없다면, raw data를 get()로 키값을 조회하고 default값을 None으로 두게됨
  • put 요청시 view에서 save()메서드를 호출 -> update()메서드가 호출되며 이에 따라서 validate_data, instance 인자를 통해서 적절히 기존 값을 넣을지 아니면 변경된 값이라면 그 값을 조회해서 사용할지 판단하게 되요.
    [ 예, instance.name = validated_data.get(name, instance.name)]

rooms/serializers.py

# 생략
        return Room.objects.create(**validated_data)

    def validate(self, data): 
        if not self.instance: # create() 메소드를 통해서 self.instance가 정확히 값을 None이 아닌 값을 참고 하고 있을때 작동하게 만듬
        if self.instance: # update 하는 경우, db에 pk로 값을 조회 했을때 반환받은 값이 있는 경우 
            check_in = data.get('check_in', self.instance.check_in)
            check_out = data.get('check_out', self.instance.check_out)
        else:             # create 하는 경우
            check_in = data.get('check_in')
            check_out = data.get('check_out')
            if check_in == check_out:
                raise serializers.ValidationError('Not enough time between changes')
        return data

    def update(self, instance, validated_data): # validate() 메서드로 검증된 데이타가 3번째 인자로 들어오게되요.
        print(instance, validated_data) 
        instance.name = validated_data.get('name', instance.name)
        instance.address = validated_data.get('address', instance.address)
        instance.price = validated_data.get('price', instance.price)
        instance.beds = validated_data.get('beds', instance.beds)
        instance.lat = validated_data.get('lat', instance.lat)
        instance.lng = validated_data.get('lng', instance.lng)
        instance.bedrooms = validated_data.get('bedrooms', instance.bedrooms)
        instance.bathrooms = validated_data.get('bathrooms', instance.bathrooms)
        instance.check_in = validated_data.get('check_in', instance.check_in)
        instance.check_out = validated_data.get('check_out', instance.check_out)
        instance.instant_book = validated_data.get(
            'instant_book', instance.instant_book)
        instance.save()
        return instance 

rooms/views.py

                return Response(status=status.HTTP_403_FORBIDEN)
            serializer = WriteRoomSerializer(room, data=request.data, partial=True) # partial 사용해서 기본적으로 required fields를 모두 넣지 않아도 오류를 발생 시키지 않게함
            if serializer.is_valid(): # db에 가져온 객체 정보와 클라이언트로부터 가져온 정보를 serializer와 비교했을때 유효한가?
                room = serializer.save()     # update() 메서드를 호출함.  객체 생성후 db에 저장함
                return Response(ReadRoomSerializer(room).data) # statusz 키워드를 적어주지 않아도 기본값은 200이라서 생략 가능
            else:
                return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
            return Response()

다시 한번 정리하자면 views.py의 WriteRoomSerializer(room, ...) room은 두 곳으로 빠지게 됩니다.

  • 하나 validate()메서드로 빠져서 self.instance의 속성값 존재 여부를 확인하고 기타 로직이 작동되요.
  • 둘, update메서드에서 validated_data.get()메서드의 키가 없을 경우 할당하게될 변수의 값으로 넘어가게되요.
profile
어제보다 오늘 그리고 오늘 보다 내일...

0개의 댓글