수학과의 좌충우돌 프로그래밍

[Django] Serializer 를 통한 유효성 검사 및 저장 본문

웹프로그래밍/DRF

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

ssung.k 2019. 12. 30. 04:47

이번 포스팅에서는 Serializer 에 대해서 자세히 알아보도록 하겠습니다.

Serializer

우선 github에서의 Serializer 코드를 살펴보겠습니다.

SerializerBaseSerializer 를 상속 받고 BaseSerializer 의 생성자는 아래와 같습니다.

# rest_framework/serializers.py

class BaseSerializer(Field):
    def __init__(self, instance=None, data=empty, **kwargs):
        # 생략
        
class Serializer(BaseSeializer):
  # 생략

 

그렇기 때문에 우리는 이를 사용할 때 post 라는 instance가 먼저 나온다면 뒤에 data 라는 keyword 를 써줄 필요가 없지만 instance 없이 data 를 파라미터로 넘기기 위헤서는 keyword 가 꼭 필요합니다. 이러한 이유로 data 라는 keyword를 써주도록 합시다.

serializer = PostSerializer(post)
serializer = PostSerializer(data=request.data)
serializer = PostSerializer(post, data=reqeust.data)
serializer = PostSerializer(post, reqeust.data)
serializer = PostSerializer(reqeust.data) # 오류

이 부분이 익숙하지 않다면 python의 함수 파라미터 부분을 다시 공부하시길 바랍니다.

 

data 에 파라미터가 주어지면 다음과 같은 과정을 거치게 됩니다.

  1. serializer.is_valid() 가 호출 되었을 시,
  2. serializer.initial_data 에 data 값을 넣어주고
  3. serializer.validated_data 에 유효성 검증을 통과한 값들을 넣어주고 serializer.save() 시 이 값들을 저장하게 된다.
  4. serializer.errors 에는 유효성 검사에서의 오류들이
  5. serializer.data 에는 유효성 검사 후 인스턴스 값이 사전으로 저장된다.

 

serializer.save()

serializer.save() 는 다음과 같이 **kwargs 를 받습니다.

def save(self, **kwargs):
  pass

이 경우에 유효성을 통과한 validated_datakwargs dict가 합쳐져서 DB에 저장을 하게 됩니다.

이 때, self.instance가 에 따라 저장하는 방식이 다릅니다.

  • self.instance 값이 있을 때 : update() 를 통해서 저장
  • self.instance 값이 없을 때 : create() 를 통해서 저장

 

Validators

serializer 에서도 validator를 제공해주고 있습니다.

django의 기본 validator 와 더불어 유일성을 확인해주는 것도 제공해주고 있습니다.

간단히 하나면 살펴보겠습니다.

  • UniqueValidator

    • 지정 필드가 queryset 에서 Unique한지 확인을 해주게 됩니다.
    • 필드에 대해서 unique=True 를 지정한다면 자동으로 추가됩니다.

 

유효성 검사 예외

rest_framework.exceptions.ValidationError 를 사용하며 이는 응답 상태코드 400으로 처리합니다.

 

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

유효성 검사 함수는 model 에서 지정하는 것을 추천드립니다.

model 에서 유효성 검사 함수 지정하기

 

[Django] Form 과 validations

django 로 프로젝트를 진행하면서 예상 외의 난관에 봉착했습니다. 회원가입을 구현하는 과정에서 회원가입의 input 이 너무나도 많았습니다. 이를 하나씩 input 태그를 만들어주고 views 에서 값을 하나씩 받아오..

ssungkang.tistory.com

지금은 간단히 Serializer 에서 하는 예시를 하나 알아보도록 하겠습니다.

  • Field 에 대한 validator

    • 함수이름 : validate_{field name}

    • 함수의 인자로 해당 값이 전달됩니다.

      # serializers.py
      
      from rest_framework import serializers
      from rest_framework.exceptions import ValidationError
      
      class PostSerializer(serializers.Serializer):
          title = serializers.CharField(max_length=100)
      
          def validate_title(self, value):
              if '제목' not in value:
                  raise ValidationError('제목이라는 말이 들어가야 합니다.')
              return value
      

      serializers.Serializer 가 아닌 serializers.ModelSerializer 의 경우에는 7번째 줄의 필드 지정 없이 Meta 클래스로 model과 fields 를 지정해주면 됩니다.

  • object 에 대한 validator

    • 함수이름 : validate

    • 함수의 인자로 해당 값이 전달됩니다.

      class PostSerializer(serializers.Serializer):
          title = serializers.CharField(max_length=100)
      
          def validate(self, data):
              if '제목' not in data['title']:
                  raise ValidationError('제목이라는 말이 들어가야 합니다.')
              return data
      

 

perfrom 함수

사용자에게 입력을 받은 후, 저장할 시에는 추가적인 정보와 함께 저장해야 하는 경우도 있습니다.

예를 들어 사용자로 부터 글의 제목을 받은 후 사용자의 ip 를 얻기 위해서는 이 정보를 사용자가 입력해야할까요?

그렇지 않습니다. 이는 따로 커스튬을 해줘야하는데 그 부분을 알아보도록 하겠습니다.

 

mixins 에서 CreateModelMixin 을 살펴보면 실질적인 저장은 perform_create 함수를 통해서 이루어지는 것을 알 수 있습니다.

# 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()

나머지도 이와 마찬가지입니다. 따라서 추가적인 커스튬이 필요하다면 perform 함수를 수정해줍니다.

  • 생성 : perform_create
  • 수정 : perform_update
  • 삭제 : perform_destroy

 

아래와 같이 model 을 정의하고,

# models.py

from django.db import models

class Post(models.Model):
    title = models.CharField(max_length=100)
    ip = models.GenericIPAddressField()
    created_at = models.DateTimeField(auto_now_add=True)
    updated_at = models.DateTimeField(auto_now=True)

 

title의 값만 사용자로 부터 받습니다.

# serializers.py

from rest_framework.serializers import ModelSerializer
from .models import Post

class PostSerializer(ModelSerializer):
    class Meta:
        model = Post
        fields = ['title']

 

그 후 ip 를 추가하기 위해서는 perform_create 를 커스튬해줍니다.

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

 

'웹프로그래밍 > DRF' 카테고리의 다른 글

[Django] Throttling  (0) 2020.01.08
[Django] Authentication 과 Permissions  (0) 2020.01.08
[Django] DRF Pagination  (6) 2019.12.30
[Django] filtering 과 Search  (0) 2019.12.29
[Django] format 과 Renderer  (0) 2019.12.29
Comments