(DRF) Serializer Validation

duo2208·2022년 1월 31일
1

Django

목록 보기
17/23
post-thumbnail
post-custom-banner
🚀 (DRF API Guide) Validators
REST 프레임워크에서 유효성 검사를 처리하는 대부분의 경우 단순히 기본 필드 유효성 검사에 의존하거나 직렬 변환기 또는 필드 클래스에 대한 명시적 유효성 검사 방법을 작성합니다.

Serializer 의 생성자


Serializer 의 생성자

SerializerBaseSerializer 를 상속받습니다.

""" Serializer """
# rest_framework/serializers.py
class BaseSerializer(Field):
	def __init__(self, instance=None, data=empty, **kwargs)
    
class Serializer(BaseSerializer):
	pass

+ data = 인자가 주어지면

다음과 같은 절차를 거칩니다.

  1. serializer.is_valid() 호출 시,
  2. serializer.initial_data 에 data 값을 넣어주고
  3. serializer.validated_data 를 통해 유효성 검증에 통과한 값들이 serializer.save() 시에 사용됩니다.
  4. serializer.errors 에는 유효성 검증 후 오류 내역들이 저장되고
  5. serializer.data 에는 유효성 검증 후 갱신된 인스턴스에 값이 사전형으로 저장됩니다.

+ serializer.save()

def save(self, **kwargs):
	pass

serializer.save(**kwargs) 를 호출을 하면

  1. DB에 저장한 관련 인스턴스를 리턴해주고

  2. .validated_datakwargs 사전을 합친 데이터를 DB로 저장하기 위한 시도를 합니다. 저장 방식은 self.instance가 어떤가에 따라 다릅니다.

    ›› self.instance 인자를 지정했을 때 : .update() 를 통해 저장
    ›› self.instance 인자를 지정하지 않았을 때 : .create() 를 통해 저장



Serializer Validators


rest_framework.validators

Serializer 에서 제공하는 validator 는 다음과 같습니다. 값에 대한 유효성 검사를 수행하는 django 기본 validator 와 더불어 DRF는 유일성 체크를 도와주는 validator 도 제공하고 있습니다.

  • UniqueValidator : 지정 1개 필드가 지정 QuerSet 범위에서의 유일성 여부 체크
  • UniqueTogetherValidator
  • BaseUniqueForValidator
  • UniqueForDateValidator (BaseUniqueForValidator) : 지정 날짜 범위에서 유일성 여부 체크
  • UniqueForMonthValidator (BaseUniqueForValidator) : 지정 월 범위에서 유일성 여부 체크
  • UniqueForYearValidator (BaseUniqueForValidator) : 지정 년 범위에서 유일성 여부 체크
""" UniqueValidator 예시 """
""" UniqueValidator 의 경우 모델 필드에 unique=True를 지정하면 동작하게 되어있습니다. """
# queryset (필수) : 대상 쿼리셋의 범위
# message : 유효성 검사 실패 시의 에러 메시지
# lookup : 디폴트 'exact'

from rest_framework.validators import UniqueValidator

slug = SlugField(
	max_length = 100,
 	validators = [UniqueValidator(queryset=BlogPost.objects.all())]

유효성 검사 함수를 커스텀 하고 싶을 경우 위의 예시 처럼 Serializer 필드에 직접 validators 지정을 하지만, 💡 가급적이면 model 에서 유효성 검사 함수를 지정하는 것을 추천합니다.

rest_framework.exceptions.ValidationError

유효성 검사에 실패하면 ValidationError 예외를 발생시킵니다. django 기본에서는 django.forms.exceptions.ValidationError 를 썼다면, DRF 에서는 APIException 을 상속받는 rest_framework.exceptions.ValidationError 를 사용합니다.

class ValidationError(APIException):
 	status_code = status.HTTP_400_BAD_REQUEST
 	default_detail = _('Invalid input.')
 	default_code = 'invalid'

 	def __init__(self, detail=None, code=None):
 		if detail is None:
   			detail = self.default_detail
 		if code is None:
  			code = self.default_code

 		if not isinstance(detail, dict) and not isinstance(detail, list):
   			detail = [detail]

   		self.detail = _get_error_details(detail, code)



Serializer 에서의 유효성 검사 함수 지정

그러면 Serializer 에서 유효성 검사를 하는 방법을 예시를 통해 알아봅시다. 두 가지 방법이 있습니다.

(1) Field Level 검사 : 필드 정의 시에 validator 지정

validator_필드명 을 구현해서 유효성 검사를 할 수 있습니다.

# serializers.py

from rest_framework import serializers
from rest_framework.exceptions import ValidationError

class PostSerializer(serializers.Serializser):
	title = serializers.CharField(max_length=100)
 
	def validte_title(self, value):
 		if '제목' not in value:
			raise ValidationError('제목에 필히 django 라는 말이 포함되어야합니다.')
  		return value

(2) Object Level 검사 : 클래스에 Meta.validator 지정

validate 함수를 사용해서 2개 이상의 필드에 대해 유효성 검사를 할 수 있습니다.

class PostSerializer(serializer.Serializer):
	title = serializers.CharField(max_length=100)
    
 	def validate(self, data):
  		if '제목' not in value:
			raise ValidationError('제목에 필히 django 라는 말이 포함되어야합니다.')
  		return data



Mixins 의 perform 함수


APIView 의 create / update / destory 멤버함수의 실질적인 DB 처리 반영mixinsCreateModelMixin 인의 perform_ 함수를 통해 이루어 집니다.

  • perform_create : 생성
  • perform_update : 수정
  • perform_destroy : 삭제
# rest_framework/mixins.py

class CreateModelMixin(object):
	def create(self, request, *args **kwargs):
 		serializer = self.get_serializer(data=request.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)

    
	def perform_create(self, serializer):
		serializer.save()

사용자로부터 form 을 통해 정보를 입력 받은 후, 그 이외의 추가적인 정보를 저장하고 싶을 때 perform_ 함수를 이용하면 우리가 원하는 정보를 얻을 수 있도록 커스텀할 수 있습니다. 대표적인 예로 ip가 있습니다. ip 정보를 사용자보고 입력하라고 하는건 곤란하겠죠?

+ 추가 저장 필드 예시) 유저들의 ip 저장

# models.py

from django.db import models

class Post(models.Model):
	title = models.CharField(max_length=100)
 	ip = models.GenericIPAddressField()
# serializers.py 

from rest_framework.serializers import ModelSerializer
from .models import Post

class PostSerializer(ModelSerializer):
	class Meta:
 		model = Post
    	fields = ['title']
# views.py
from rest_framework.viewsets import ModelViewSet
from .models import Post
from .serializers import PostSerializer

class PostViewSet(ModelViewSet):
	queryset = Post.objects.all()
  	serializer_class = PostSerializer
    
 	def perform_create(self, serializer):
 		serializer.save(ip=self.request.META['REMOTE_ADDR'])



Form VS Serializer 비교


생성자의 차이

""" Form """
# django/forms/forms.py
class BaseModelForm:
	def __init__(self, data=None, files=None, auto_id='id_%s', prefix=None, 
                initial=None, error_class=ErrorList, label_suffix=None, 
                empty_permitted=False, instance=None, use_required_attribute=None):

class Form(BaseForm):
	pass
""" Serializer """
# rest_framework/serializers.py
class BaseSerializer(Field):
	def __init__(self, instance=None, data=empty, **kwargs)
    
class Serializer(BaseSerializer):
	pass

유효성 검사 수행 시점

""" Form """
class ProcessFormView(View):
	def get(self, request, *args, **kwargs):
		return self.render_to_response(self.get_context_data())
        
 	def post(self, request, *args, **kwargs):
		form = self.get_form()	# POST 데이터를 통해 Form 객체 생성.
  		if form.is_valid():	# 유효성 검사 수행. 실패하면 False 반환.
 			return self.form_valid(form)	# DB로의 저장 수행.
 		else:
  			return self.form_invalid(form)	# 오류 HTML 응답.
""" Serializer """
# rest_framework/mixins.py
class CreateModelMixin(object):
	def create(self, request, *args, **kwargs): # POST요청 -> CREATE 요청이 들어오면
		serializer = self.get_serializer(data=request.data) # POST 데이터를 통해 Serializer 인스턴스를 생성하고
 		serializer.is_valid(raise_exception=True) # 유효성 검사를 수행. 실패하면 예외발생.
   		self.perform_create(serializer)	# DB로의 저장 수행.
  		headers = self.get_success_headers(serializer.data) # 필요한 헤더를 뽑고,
   		return Response(serializer.data, status=status.HTTP_201_CREATED, headers=headers) # 응답을 한다.
    
	def perform_create(self, serializer):
		serializer.save()

커스텀 유효성 검사 루틴

""" Form """
from django import forms

class PostForm(forms.Form)
	title = forms.CharField()
    
 	def clean_title(self):
		value = self.cleaned_data.get('title', '')
   		if 'django' not in value:
  			raise forms.ValidationError('에러')
 		return value
""" Serializer """
from rest_framework.exceptions import ValidationError

class PostSerializer(serializers.Serializer):
	title = serializers(serializers.Serializer):
    
	def validate_title(self, value):
		if 'django' not in value:
  			raise forms.ValidationError('에러')
 		return value
 

📌 참고 출처

post-custom-banner

0개의 댓글