나는 게시글에 대한 onwer 필드를 User 모델과의 Foreign Key 관계로 게시글과 사용자 간에 1:N 관계를 형성했다.
# models.py
class PostModel(models.Model):
# FK (User 모델과의 관계 설정)
owner = models.ForeignKey(User, on_delete= models.CASCADE, related_name= "post_owner")
# ...
그리고 게시글 리소스의 생성(CREATE) 및 업데이트(UPDATE) 작업에서 클라이언트에서 onwer 정보 조작을 방지하기 위해, view에서 request 데이터에 owner 정보를 추가하도록 구현했다.
# views.py
class PostListCreateAPIView(generics.ListCreateAPIView):
# ...
def create(self, request, *args, **kwargs):
request.data['owner'] = request.user.pk
return super().create(request, *args, **kwargs)
하지만, DRF의 APITestCase를 통해 API End-Point 테스트 코드를 작성하고 있는 도중
CREATE 작업과 UPDATE 작업에서만 아래와 같은 오류가 발생했다.
# test case
def test_create_post_success(self):
# ...
response = self.client.post(
path= f'{BASE_API_URL}/posts',
data= {
"title": "게시글 제목",
"contents": "게시글 내용"
}
)
# Out-Put: error Messages
AttributeError: This QueryDict instance is immutable
나는 구글에 내 오류를 검색해서 나와 같이 request.data에 접근할 때
같은 오류가 발생해 질문한 게시글이 있었다.
질문자도 똑같이 request.data를 수정할 때만 문제가 발생해 질문을 올린 것 같다.
해당 Issue의 해결책을 확인하기 위해 답변 내용을 확인해봤다.
답변 내용은 APIClient가 format을 지정하지 않으면, 기본 데이터 형식인 multipart
로 전송해 request가 딕셔너리가 아닌 불변 객체 QueryDict으로 받아 발생하는 오류라고 한다.
(감사합니당~)
그래서 실제로 view에서 request.data를 찍어봤다.
# class PostListCreateAPIView(generics.ListCreateAPIView):
# ...
# def create(self, request, *args, **kwargs):
# print(request.data)
QueryDict: {'title': ['게시글 제목'], 'contents': ['게시글 내용']}>
format을 지정하지 않으면, default 값인 multipart/form-data
형식으로 데이터를 전송 있었다.
그로 인해, QueryDict
를 변경하도록 구현한 CREATE와 UPDATE 작업에서만 오류가 발생한 것이다.
첫번째 해결책으로는 APIClient의 데이터 전송 형식을 JSON으로 지정하는 것이다.
# test case
def test_create_post_success(self):
# ...
response = self.client.post(
path= f'{BASE_API_URL}/posts',
data= {
"title": "게시글 제목",
"contents": "게시글 내용"
},
format= 'json' # 데이터 형식을 json 형식으로!
)
만약에, 파일 업로드시와 같이 multipart/form-data
를 사용해야할 경우가 발생하면
똑같이 QueryDict
를 수정 해야함으로 다시 같은 오류가 발생할 것이다.
그래서 나는 두번째 방법으로 테스트 코드를 작성 했다.
두번째 해결책은 _mutable
플래그로 QueryDict
를 수정할 수 있도록 지정하는 방법이다.
# views.py
class PostListCreateAPIView(generics.ListCreateAPIView):
# ...
def create(self, request, *args, **kwargs):
setattr(request.data, '_mutable', True) # QueryDict 수정 허용
request.data['owner'] = request.user.pk
return super().create(request, *args, **kwargs)
StackOverFlow에서 답변한 것처럼 multipart
형식을 필연적으로 사용해야 할 때
_mutable
플래그로 QueryDict
를 수정할 수 있도록 정의해주면 된다.
테스트 통과!