DRF Serializer를 통한 유효성 검사 및 저장

2022. 7. 14. 16:05강의 정리/Django REST Framework

반응형

Serializer의 생성자


Serializer는 Django 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
  • Form에서는 인자로 data와 files, instance를 받는다.

 

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

# instance -> 직렬화 목적
# data -> 유효성 검사

class Serializer(BaseSerializer):
    pass

 

  • Serializer에서는 인자로 instance와 data를 받는다.

 


data = 인자가 주어지면


.is_valid()가 호출되고 나서야

 

  • .initial_data 필드에 접근할 수 있고
  • .validated_data (form에서는 cleaned_data)를 통해 유효성 검증에 통과한 값들이 .save()시에 사용됩니다.
    • form은 cleaned_data라는 똑같은 기능 존재
  • .errors → 유효성 검증 수행 후에 오류 내역
  • .data → 유효성 검증 후에, 갱신된 인스턴스에 대한 필드값 사전
    • 즉 유효성 검사를 마쳐야 위의 기능들을 활용할 수 있다.

 

우리가 Serializer를 활용하는 패턴 2가지

  • 데이터 직렬화를 사용하기 위해서
    • 직렬화가 목적인 경우 instance 인자에 model 객체 혹은 쿼리셋을 넘겨준다.
  • Form과 유사하게 입력값에 대한 유효성 검사와 유효성 검사 통과 값들을 사전 형태로 가져오고, DB로 저장하게 하는 것
    • 유효성 검사를 위해서는 instance와 data에 인자를 넘겨주어야 한다.

 

 

 


 

serializer.save(**kwargs) 호출을 할 때


  1. DB에 저장한 관련 instance를 리턴
  2. .validated_data와 kwargs사전을 합친 데이터를
    • .update 함수 / .create함수를 통해 관련 필드에 값을 할당하고, DB로의 저장을 시도
    • 그러면 언제 update와 create를 지원하는지
      • .update() : self.instance 인자를 지정했을 때
      • .create() : self.instance 인자를 지정하지 않았을 때

 

# Form에서의 데이터 save코드
form = PostForm(request.POST)
if form.is_valid():
    post = form.save(commit=False)
    post.author = request.user
    post.ip = request.META['REMOTE_ADDR']
    post.save()

# Serializer에서의 데이터 save코드
serializer.is_valid(...) # ex)raiseException
# serializer.save에 내가 업데이트 하고 싶은 인자들을 넘겨주면 된다.
serializer.save(author=request.user, ip=request.META['REMOTE_ADDR'])

 

 

 


 

from rest_framework.validators import ...


https://github.com/encode/django-rest-framework/blob/master/rest_framework/validators.py

 

GitHub - encode/django-rest-framework: Web APIs for Django. 🎸

Web APIs for Django. 🎸. Contribute to encode/django-rest-framework development by creating an account on GitHub.

github.com

 

 

  • 값에 대한 유효성 검사를 수행하는 호출 가능한 객체
  • DRF에서는 유일성 체크를 도와주는 Validators 제공
  1. UniqueValidator : 지정 1개 필드가 지정 QuerySet범위에서의 유일성 여부 체크
  2. UniqueTogetherValidator : UniqueValidator의 다수 필드 버전
  3. BaseUniqueForValidator
  4. UniqueForDateValidator (BaseUniqueValidator) : 지정 날짜 범위에서 유일성 여부 체크
  5. UniqueForMonthValidator (BaseUniqueValidator) : 지정 월 범위에서의 유일성 여부 체크
  6. UniqueForYearValidator (BaseUniqueValidator) : 지정 년 범위에서 유일성 여부 체크

 

 


 

UniqueValidator


모델 필드에 unique=True를 지정하면 자동 지정

  • queryset(필수) : 대상 쿼리셋의 범위를 지정해줌
  • message : 유효성 검사 실패 시의 에러 메세지
  • lookup : 디폴트 'exact'
    • Serializer 필드에 직접 Validators 지정 가능
    • 그러나 웬만하면 모델 필드에서 unique=True를 지정함으로써 모델 Serilizer에서 Validator가 자동으로 추가되도록 하는것을 추천
from rest_framework.validators import UniqueValidator

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

 


 

UniqueForDateValidator / Month / Year


특정 쿼리 셋에서 → 특정 날짜 범위 내에서 → 특정 필드가 유니크한지 검사

  • queryset (필수) : 대상 쿼리셋의 범위
  • field (필수)
  • date_field (필수) : Date, Month, Year값이 참조 할 날짜 필드 (필드 상에서 연/월/일 중 Validator에 맞게 참조한다.)
    • 특정 날짜의 범위 안에 있는 / 특정 쿼리셋의 범위 내에서  / 특정 필드가 Unique한지에 대해서
  • message

 

from rest_framework.validators import UniqueTogetherValidator
class ExampleSerializer(serializers.Serializer):
    # ...
    class Meta:
        validators = [
            UniqueForYearValidator( 
                queryset=BlogPostItem.objects.all(), 
                field='slug', 
                date_field='published'
            ) 
        ]

 


 

유효성 검사에 실패하면 ValidationError 예외 발생


필히 rest_framework.exception.ValidationError 사용
장고 기본에서는 django.forms.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의 유효성 검사하는 2가지 방법

  • 필드 정의시에 validators 지정하기
  • 클래스 Meta.validators 지정
  • ModelSerializer 같은 경우에는 model 단에 유효성 검사 로직 구현하기

 

Field Level 검사: 유효성 검사 및 값 변환

class PostSerializer(serializers.Serializer):
    title = serializers.CharField(max_length=100)

    def validate_title(self, value): # validator_필드명을 구현해서 유효성 검사를 진행할 수 있다. (form에서는 clean_필드명)
        if 'django' not in value:
            raise ValidationError('제목에 필히 django가 포함되어야합니다.')  # 조건에 맞지않으면 rest_framework의 validationError를 발생시킨다. (form에 있는 validationError랑 헷갈리면 안됨)
        return value

 

 

Object Level 검사: 유효성 검사 및 값 변환

class PostSerializer(serializers.Serializer):
    title = serializers.CharField(max_length=100)

    def validate(self, data):  # 2개 이상의 필드에 대해 유효성 검사 로직은 validate를 구현하면 된다.(form에서는 clean)
        if 'django' not in data['title']:
            raise ValidationError('제목에 필히 django가 포함되어야합니다.')
        return data

 


DB로의 반영과 Mixins의 perform_ 계열 함수

 


APIView의 create/update/destroy 멤버함수에서 실질적인 DB처리 로직은

 

  • perform_create(serializer)
  • perform_update(serializer)
  • perform_destroy(instance)
  • 를 통해 이루어진다.

 

class CreateModelMixin:
    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()  # 모델 객체가 만들어지고, 모델 객체의 값들이 할당이 다 된 model.save()가 호출이 되는 것이다.

    def get_success_headers(self, data):
        try:
            return {'Location': str(data[api_settings.URL_FIELD_NAME])}
        except (TypeError, KeyError):
            return {}

 

 

create 시에 추가로 저장할 필드가 있다면?

  • 모델에 ip 필드가 있고, 유저의 아이피들 저장하고 싶다면?
# CreateModelMixin에서 상속받은 뷰에서 아래와 같이 사용한다.

def perform_create(self, serializer):  # 실제 APIView구현에서 
   author = self.request.user
   ip = self.request.Meta['REMOTE_ADDR']
   serializer.save(author=author, ip=ip)

 

 


 

User가 data를 수정이나 읽지 못하게 하는 방법

  1. Serializer에서 ReadOnly를 걸거나
  2. model 필드 옵션으로 editable=False로 설정
반응형

'강의 정리 > Django REST Framework' 카테고리의 다른 글

DRF Pagination  (0) 2022.07.14
DRF Authentication과 Permission  (0) 2022.07.14
From과 Serializer 관점에서 DRF 비교  (0) 2022.07.13
DRF ViewSet과 Router  (0) 2022.07.13
DRF mixins 상속을 통한 APIView  (0) 2022.07.12