포스트

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

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_dataserializer.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 로직을 사용한다.

Djagno ValidationError

DRF ValidationError



💡 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)를

통해 이뤄 집니다.

CreateModelMixin 소스코드

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'])

이 기사는 저작권자의 CC BY 4.0 라이센스를 따릅니다.