Serializer를 통한 유효성 검사 및 저장
💡 생성자
Serialize의 역할은 요청받은 데이터를 직렬화 해주고, form과 유사하게
입력받은 값의 유효성 검증하고, 검증에 통과한 값들을 사전 형태로 가져오고,
가져온 값들을 DB에 저장하는 역할까지 해준다.
Serializer는 Djagno Form과 컨셉 및 사용법이 매우 유사하다.
하지만 생성자(__init__)에 차이가 있다.
여기서 둘의 차이점은 Form 생성자의 첫 번째 인자는 data이지만,
Serializer 생성자의 첫 번째 인자는 instance이다.
instance 인자는 직렬화가 목적이고, ‘모델 객체’ 혹은 ‘Queryset’을 전달해준다.
그리고 실제 유효성 검사를 위해서는 data 인자를 넘겨줘야 한다.
1
2
3
4
5
6
7
8
9
10
# django/forms/forms.py/BaseForm
class BaseForm(RenderableFormMixin):
# 생략
def __init__(self,data=None,):
# 생략
# rest_framework/serializers.py/BaseSerializer
class BaseSerializer(Field):
def __init__(self, instance=None, data=empty, **kwargs):
# 생략
Serializer의 data 인자가 주어지면, 아래와 같은 기능을 사용할 수 있다.
1. .initial_data 필드에 접근할 수 있다.
2. .validate_data 를 통해 유효성 검증에 통과한 값들이 .save() 시에 저장
form은 cleaned_data
3. .errors → 유효성 검증 수행 후에 오류 내역
4. .data → 유효성 검증 후에, 갱신된 인스턴스에 대한 필드값 사전으로 반환
💡 serializer.save() Method
serializer.save() 를 호출할 때는 여러 kwargs를 넘겨줄 수 있다.
실제 위 함수는 ModelForm.save()와 마찬가지로 아래와 같은 기능을 수행한다.
▪ DB에 저장한 관련 Instance를 리턴
▪ .validated_data 와 serializer.save() 의 인자로 주어진 kwargs 사전을
합친 데이터를 실제 DB에 업데이트를 시도.
예를 들어 사용자의 IP를 지정해야 할 경우 보통 아래와 같이 구현했다.
왜냐면 사용자가 직접 IP를 입력하는 경우를 없애기 위해서.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# views.py
class PostCreate(request):
if reqeust.method == 'POST':
form = PostForm()
if form.is_valid():
# DB 저장 보류
post = form.save(commit=False)
# 업데이트 해야할 부분 추가.
post.author = request.user
post.ip = request.META['REMOTE_ADDR']
# 최종 저장.
post.save()
return redirect('instagram:test')
반면에 Serializer에서는 save 매소드에 내가 업데이트하고 싶은 값 들을 kwargs로 넘겨준다.
1
2
serializer.is_valid(raise_exception=True)
serializer.save(author=request.user, ip=request.META['REMOTE_ADDR'])
▪ .update() : self.instance 인자가 지정되어 있을 경우
▪ .create() : self.instance 인자가 지정되어 있을 않을 경우
💡 rest_framework.validators
DRF에서는 유일성 체크를 도와주는 validators를 제공해준다.
▪ UniqueValidator : 지정 1개 필드가 지정 QuerySet 범위에서의 유일성 여부 체크
▪ UniqueTogetherValidator : UniqueValidator의 다수 필드 버전
▪ BaseUniqueForValidator
▪ UniqueForDateValidator (BaseUniqueValidator) : 지정 날짜 범위에서 유일성 여부 체크
▪ UniqueForMonthValidator (BaseUniqueValidator) : 지정 월 범위에서 유일성 여부 체크
▪ UniqueForYearValidator (BaseUniqueValidator) : 지정 년 범위에서 유일성 여부 체크
UniqueValidator
Serializer 필드에 직접 Validators를 지정 가능하다.
1
2
3
4
5
6
from rest_framework.validators import UniqueValidators
slug = SlugField(
max_length = 100,
validators = [UniqueValidaors(queryset=...)]
)
그러나 모델 필드에 unique=True를 지정하면, 자동 지정된다.
▪ queryset → 필수
▪ message : 유효성 검사 실패 시의 에러 메세지
▪ lookup : default(‘exact’)
UniqueTogetherValidator
모델 Meta에 unique_together이 지정된다면, 자동 지정된다.
1
2
3
4
5
6
7
8
9
10
11
from rest_framework.validators import UniqueTogetherValidator
class ExampleSerializer(serializers.Serializer):
# ...
class Meta:
validators = [
UniqueTogetherValidator(
queryset=ToDoItem.objects.all(),
fields=['list', 'position']
)
]
▪ queryset → 필수
▪ fields → 필수
▪ message
UniqueFor(Date/Month/Year)Validator
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
from rest_framework.validators import UniqueForYearValidator
class ExampleSerializer(serializers.Serializer):
# ...
class Meta:
validators = [
UniqueForYearValidator(
# 1. 전체 queryset에서
queryset=BlogPostItem.objects.all(),
# 2. published라는 field에서 연도만 확인하고,
date_field='published'
# 3. 해당 년도에 slug가 유일한지 체크!
field='slug',
)
]
▪ queryset → 필수
▪ date_field → 필수
▪ fields → 필수
▪ message
💡 ValidationError
Django와 DRF의 ValidationError는 이름만 같을 뿐, 다른 로직을 사용하게 된다.
기본 Django에서는 djagno.core.exceptions.ValidationError 로직을,
DRF에서는 rest_framework.exceptions.ValidatorError 로직을 사용한다.
💡 Serializer에서 유효성 검사
필드 정의 시에 validators 지정하거나, 클래스 Meta.validators 지정할 수 있다.
1. Field Level 검사
특정 필드에 대한 유효성 검사 및 값 변환
▪ validate_필드명
1
2
3
4
def validate_title(self,value):
if 'django' not in value:
raise ValidationError("에러 발생")
return value
2. Object Level 검사
두 개 이상의 필드에 대한 유효성 검사 및 변환
1
2
3
4
def validate(self,value):
# 생략
#-- form에서는 clean()
💡 DB반명 & perform_ 계열 함수
APIView의 create/update/destroy 멤버 함수에서 실질적인 DB처리 로직은
perform_create(serializer), perform_update(serializer), perform_destroy(instance)를
통해 이뤄 집니다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
class CreateModelMixin:
"""
Create a model instance.
"""
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() #=> 실행 시, 모델 객체 생성
def get_success_headers(self, data):
try:
return {'Location': str(data[api_settings.URL_FIELD_NAME])}
except (TypeError, KeyError):
return {}
▪ perform_create(serializer) : 실제로 create를 수행하는 함수
▪ serializer.save() : 모델 객체 생성
create시에 추가로 저장할 필드가 있을 경우에는 아래와 같이 진행한다.
1
2
def perform_create(self, serializer):
serializer.save(ip=seld.requet.META['REMOTE_ADDR'])