19. Validation

data_hamster·2023년 4월 30일
0
post-custom-banner

학습주제
Validation

학습내용
Vote 목록, vote 생성, 수정을 구현했었다.

문제.
user1은 특정 질문에 대한 답변 휴가 - 바다 을 한 상황에서 휴가 - 산을 POST하면

이미 question에 대해 voter가 생성되어 있는데, 또 생성하려했기 때문.

500 에러를 띄워준다
서버에서 오류가 발생했다는 의미. 투표를 한 질문에 대해 또 투표를 하는 행위는 500에러가 발생해서는 안된다. 사용자에게 책임이 있다는 400대 에러를 내려야 한다.

문제2.
휴가 - 아니오 처럼 전혀 엉뚱한 답을 할 경우에도 만들어버린다. 원래라면 에러를 발생시켜야 한다.

VoteSerializer에서 validators를 정의해서 우리가 question과 voter가 이미 존재하는 경우 validation을 통과시키지 않으려 한다.

    class Meta:
        model = Vote
        fields = ['id', 'question', 'choice', 'voter']
        validators = 

is_valid가 어디서 호출되는지 알아봐야한다.

여기서 호출되고 있다. .is_valid에서 validation을 수행하는 방법을 시리얼라이저 손자 클래스에서 다시 재정의하려고 한다.
from rest_framework.validators import UniqueTogetherValidator

    class Meta:
        model = Vote
        fields = ['id', 'question', 'choice', 'voter']
        validators = [
            UniqueTogetherValidator(
                queryset=Vote.objects.all()
                fields=['question', 'voter']
            )
        ]

전체 쿼리셋을 불러온뒤 fields에서 어떤 필즈들을 함께 봐서 유니크한지 체크


voter 필드가 비었다고 나온다. 그 이유는 generis - mixins - CreateModelMixin - create에서 확인할 수 있다. 우리가 voter를 넣어준 것은 perform_create단계였었다.

validation을 수행하는 단계는 그 윗줄이다. .is_valid를 할때는 우리가 voter를 알 방법이 없다. VoteSerializer에서 voter는 ReadOnly이기 때문이다. 따라서 perform_create단계를 validate 전에 해야한다. 그래서 perform_create을 오버라이드를 하는 것이 아니라 create를 오버라이드 하도록 한다.

perform_create부분을 is_valid 전에 해야한다. 시리얼라이저가 is_valid를 할 때 voter가 있는지를 보기 때문. voter와 question이 유니크한지 확인하는 로직을 넣었었기 때문.

    def create(self, request, *args, **kwargs):
        new_data = request.data.copy()
        new_data['voter'] = request.user.id
        serializer = self.get_serializer(data=new_data)
        serializer.is_valid(raise_exception=True)
        self.perform_create(serializer)
        headers = self.get_success_headers(serializer.data)
        return Response(serializer.data, status=status.HTTP_201_CREATED, headers=headers)

perform_create 오버라이드할 때는 self.request.user 로 넣었었는데, perform_create시에 리퀘스트가 주어지지 않기 때문에 self에서 가져온 것. create에선 그냥 request가 주어지기 때문에 바로 사용하였음.
이렇게 voter를 기록한 new_data를 serializer = self.get_serializer(data=request.data)
이렇게 new_data를 넣어주어도, voter를 readonly로 하였다면 serializer.is_valid를 할 시 voter를 고려하지 않기 때문에 저 voter = readonly설정을 지워줘야 한다.
그래야 이 voter값이 시리얼라이저에게 주어져서 받아온 new_data의 voter와 비교를하게 됨.


ReadOnly를 지우니 Voter를 바꿀 수 있게 되었다. 이는 create에서 다시 바꿀 예정이다.
ReadOnly가 해제되어 시리얼라이저에 기존 voter값을 is_valid에서 쓸 수 있다.


좀전에 admin2로 POST를 날렸지만 그 결과에선 voter가 wook으로 바뀌어 있다.
데이터를 오버라이드 하면서 현재 로그인한 사용자로 바꾸었기 때문.


voter를 읽기전용에서 해제 하니까 voter를 바꿀 수있게 되어버렸다. voter 바꾸어버리니까 현재 로그인 한 사용자와 voter가 달라 편집화면이 사라졌다.

Update를 할 때 사용자를 바꿀 수 있게 되어버리는 문제가 발생했다.
create를 할 때 voter를 강제로 지정해주는 작업이 필요하다.

perform_create를 오버라이드 했던 것과 마찬가지로 VoteDetail에서 어느 부분을 오버라이드 할 지 찾아본다.

여기에도 perform_update가 있는 것이 보인다. 여기서도 save를 할 때 voter를 강제로 지정해주면 될꺼 같다.
굳이 .is_valid 전에 할 필요는 없을 것 같다. 이번엔 저장만 잘 되면 되기 때문이다.

class VoteDetail(generics.RetrieveUpdateDestroyAPIView):    
    queryset = Vote.objects.all()
    serializer_class = VoteSerializer
    permission_classes = [permissions.IsAuthenticated, IsVoter]

    def perform_update(self, serializer):
        serializer.save(voter=self.request.user)



Voter를 바꾸어도 잘 들어간다.

Question - Choice 불일치 방어



이 작업은 RegisterSerializer때와 유사하다
validate라는 메소드를 오버라이드 해서 지난번에 비밀번호1, 비밀번호2가 일치하는지를 확인했었다.

그대로 메소드를 가져와서

이 validate은 serializer에 정의되어 있다

오버라이드 한 것.

attr은 choice 객체, question 객체, voter객체 를 생성하는데 이때 choice의 question 아이디와 question 의 id를 비교한다.

profile
반갑습니다 햄스터 좋아합니다
post-custom-banner

0개의 댓글