Serializers

Serialize(직렬화)

쿼리셋,모델 인스턴스 등의 complex type(복잡한 데이터)를 JSON, XML등의 컨텐트 타입으로 쉽게 변환 가능한 python datatype으로 변환시켜줌

Deserialize

받은 데이터(크롤링시 parse사용>python datatype)를 validating 한 후에 parsed data를 complex type으로 다시 변환
이때는 반드시 is_valid()를 호출하여 검사하자


인스턴스 생성 (== model)

Serializer의 생성자는 아래 코드와 같이 첫번째 인자로 instance를 받으며, 두번째 인자로 data를 받는다.

# rest_framework/serializers.py

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

class Serializer(BaseSeializer):
  # 생략

############

#DB
from datetime import datetime

class Comment:
    def __init__(self, email, content, created=None):
        self.email = email
        self.content = content
        self.created = created or datetime.now()

#원하는곳에서의 인스턴스생성
comment = Comment(email='leila@example.com', content='foo bar')

Serialize 생성

#serializers.py
from rest_framework import serializers

class CommentSerializer(serializers.Serializer):
    email = serializers.EmailField()
    content = serializers.CharField(max_length=200)
    created = serializers.DateTimeField()

serialize 하기


serializer = CommentSerializer(comment)
serializer.data

# {'email': 'leila@example.com', 'content': 'foo bar', 'created': '2016-01-27T15:17:10.375877'}

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) # 오류

data= 인자가 주어지면, 다음 순서로 처리된다.

.is_valid()가 호출이 되면

.initial_data 필드에 접근할 수 있고,

.validated_data 를 통해 유효성 검증에 통과한 값들에 대한 사전에 접근. .save()시에 사용됨.

.errors : 유효성 검사에 대한 오류 내역

.data : 유효성 검사 후에, 갱신된 인스턴스에 대한 필드값 사전


deserialize

  • Parsing된 데이터(Python datatype)을 is_valid()해주고 추후 save()시에 qs로 가능
import io
from rest_framework.parsers import JSONParser

stream = io.BytesIO(json) #JSON 문자열을 바이트 타입으로 바꾸고, ByteIO 객체로 바꾼다.
data = JSONParser().parse(stream) #JSONParser 의 parser() 메서드를 이용하여 딕셔너리 형태로 변환한다.
serializer = CommentSerializer(data=data)
serializer.is_valid()
# True
serializer.validated_data
# {'content': 'foo bar', 'email': 'leila@example.com', 'created': datetime.datetime(2012, 08, 22, 16, 20, 09, 822243)}

Saving instances

class CommentSerializer(serializers.Serializer):
    email = serializers.EmailField()
    content = serializers.CharField(max_length=200)
    created = serializers.DateTimeField()

    def create(self, validated_data):
        return Comment(**validated_data)

    def update(self, instance, validated_data):
        instance.email = validated_data.get('email', instance.email)
        instance.content = validated_data.get('content', instance.content)
        instance.created = validated_data.get('created', instance.created)
        return instance

# .save() will create a new instance.
serializer = CommentSerializer(data=data)

# .save() will update the existing `comment` instance.
serializer = CommentSerializer(comment, data=data)

serializer.save()

# save()에 추가적인 attribute 사용가능
serializer.save(owner=request.user)

save()가 호출되면, 유효성 검사를 통과한 .validated_data와 kwargs dict를 합쳐서 DB로의 저장을 시도한다. 이 때, self.instance의 유무에 따라 저장하는 방식이 다르다.

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

Validators

DRF에서는 유일성 여부 체크를 도와주는 Validator를 제공하며, queryset 범위를 제한하여 지정 범위 내에서의 유일성 여부를 체크 가능



유효성 검사 예외

rest_framework.exceptions.ValidationError를 기본으로 사용하며, 이는 응답 상태코드 400으로 처리한다.

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

** ModelSerializer를 사용한다면, 유효성 검사 함수는 모델 측에 지정하는 것이 관리측면에서 좋다.

아래에서는 모델 측이 아닌 Serializer에서 유효성 검사를 하는 예시를 알아본다.

  1. 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
  1. 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

DB 반영을 돕는 perform 함수

사용자의 입력과 함께 추가적인 정보(예를 들면, 사용자의 ip)를 함께 DB에 저장해야 하는 경우, perform 함수를 재정의해 커스튬해야한다.

먼저, 아래와 같이 model을 정의하고 title의 값만 사용자로부터 입력받는다.

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


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

'Study > Django' 카테고리의 다른 글

[Django] Views, Generic Views, Viewset  (0) 2021.05.11
[Django] settings.py - cors, static path  (0) 2021.05.11
[Django] models.py  (0) 2021.05.10
[Django] app, mysql 추가 및 연결  (0) 2021.05.10
[Django + pycharm] 개발환경 세팅  (0) 2021.05.02

+ Recent posts