Issue Report - QueryDict is Immutable

김태인·2022년 7월 13일
0

Issue Report, Debug

목록 보기
2/10

🚩 상황

  • 게시글을 작성하는 post 메서드 API를 작성한 뒤, POSTMAN으로 테스트까지 마쳤다.
  • 테스트코드를 공부하면서 적용해보기 위해서 테스트코드를 작성했는데, 테스트코드가 view의 코드를 참조하는 도중 에러가 발생하면서 테스트가 실패한다
  • 여전히 서버 코드를 실행하는데 있어서는 문제가 없지만, 테스트코드는 제대로 작동하지 않는 상태이다
# webmaster.view
class NoticeView(APIView):
    def post(self, request):
        request.data['user'] = request.user.id
        notice_serializer = NoticeSerializer(data=request.data)
        if notice_serializer.is_valid():
            notice_serializer.save()
            return Response({"message" : "공지사항 작성에 성공했다북!"}, status=status.HTTP_200_OK)
        else:
            print(notice_serializer.errors)
            return Response({"message" : "공지사항 작성에 실패했다북..."}, status=status.HTTP_400_BAD_REQUEST)
# webmaster.test
class NoticeTest(APITestCase):
    @classmethod
    def setUpTestData(cls):
        cls.user_data = {'username' : 'heejeong', 'password': '1234'}
        cls.notice_data = {'title' : '안녕' , 'content' : '반갑습니다'}
        cls.user = UserModel.objects.create_user('heejeong', '1234')

    def setUp(self):
        self.access_token = self.client.post(reverse('token_obtain_pair'), self.user_data).data['access']

    # 공지사항 목록 조회 API 
    def test_list_notice(self):
        response = self.client.get(reverse('webmaster:list_notice'), self.data)
        self.assertEqual(response.status_code,200)
        
    # 공지사항 작성하기 API
    def test_post_notice(self):
        response = self.client.post(
            path = reverse("webmaster:notice"), 
            data = self.notice_data,
            HTTP_AUTHORIZATION = f"Bearer {self.access_token}"
            )
        self.assertEqual(response.status_code, 200)

🚩에러코드

AttributeError: This QueryDict instance is immutable

🚩트러블슈팅

This QueryDict instance is immutable

  • 해당 오류는 QueryDict가 변경할 수 없는 자료형(immutable) 이라고 한다.
  • 오류코드를 Stacktrace 해보니, views.py에서 request.data 가 QueryDict였다.
  • QueryDict는 Immutable 자료형이기 때문에 테스트코드는 중간에 request.user라는 데이터를 새로운 키로 삽입할 수 없다는 것이었다!
  • 하지만, 서버에서는 정상적으로 작동하는데? 도대체 무엇이 문제인걸까…
  • 일단 테스트코드의 오류를 해결하기 위해서는 스택오버플로우의 내용처럼 mutable의 자료형으로 새로 가져오기로 했다
def post(self, request):
				'''
				기존코드
        request.data['user'] = request.user.id
        notice_serializer = NoticeSerializer(data=request.data)
				'''
				print(request.data) # 테스트코드 상에서 QueryDict인 request.data에서 사용할 수 있는 메서드 확인
				request_data_copy = request.data.copy() # mutable 한 딕셔너리로 카피하는 메서드
				request_data_copy['user'] = request.user.id
				notice_serializer = NoticeSerializer(data=request_data_copy)
        ...
				생략

🚩해결

📌 서버코드와 테스트코드의 결과가 다르다면 에러 케이스를 찾아낸 것이다!
  • 하나의 새로운 에러 케이스가 생성된 것이라고 봐야 함
  • 헤더에 Application JSON 을 넣어줄 수도 있고,
  • QueryDict를 mutable로 사용할 수 있도록 copy 메서드를 사용
📌 immutable QueryDict를 받는다면, 수정하지 않고 데이터를 삽입하자
  • is_valid()에서 해당 Model의 필드들을 검증해주는데
    • created_at, updated_at은 자동 생성 되기 때문에 패스 되고,
    • user는 Foreignkey라 이미 검증이 다 되어있는 필드임.
    • 그렇기 때문에 title필드와 content필드 만을 검증하게 된다.
  • title과 content가 is_valid()를 통과하면 그 값들과 user 값을 함께 저장해준다
def post(self):
	notice_serializer = NoticeSerializer(data=request.data)
	if notice_serializer.is_valid():
		notice_serializer.save(user=self.request.user)
		return Response({"message" : "공지사항 작성에 성공했다북!"}, status=status.HTTP_200_OK)
  else:
      print(notice_serializer.errors)
      return Response({"message" : "공지사항 작성에 실패했다북..."}, status=status.HTTP_400_BAD_REQUEST)
profile
코딩이 취미가 되는 그날까지

0개의 댓글