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) 호출을 할 때
- DB에 저장한 관련 instance를 리턴
- .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
- 값에 대한 유효성 검사를 수행하는 호출 가능한 객체
- DRF에서는 유일성 체크를 도와주는 Validators 제공
- UniqueValidator : 지정 1개 필드가 지정 QuerySet범위에서의 유일성 여부 체크
- UniqueTogetherValidator : UniqueValidator의 다수 필드 버전
- BaseUniqueForValidator
- UniqueForDateValidator (BaseUniqueValidator) : 지정 날짜 범위에서 유일성 여부 체크
- UniqueForMonthValidator (BaseUniqueValidator) : 지정 월 범위에서의 유일성 여부 체크
- 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를 수정이나 읽지 못하게 하는 방법
- Serializer에서 ReadOnly를 걸거나
- 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 |