DRF의 다양한 Serializers Class를 알아보겠다. 공식문서를 바탕으로 이 포스팅은 작성되었다.
1. Declaring Serializers
공식 문서의 예제를 살펴보자
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')
우선 직렬화할 모델과 개체를 선언했다. 그리고 이걸 Serializer Class로 선언해보자.
from rest_framework import serializers
class CommentSerializer(serializers.Serializer):
email = serializers.EmailField()
content = serializers.CharField(max_length=200)
created = serializers.DateTimeField()
2. Serializing Objects
위에서 클래스를 선언한 뒤 Serializer 개체로 만들어보자. 이는 Django의 Form 클래스를 사용하는 것과 유사하다.
serializer = CommentSerializer(comment)
serializer.data
# {'email': 'leila@example.com', 'content': 'foo bar', 'created': '2016-01-27T15:17:10.375877'}
모델 인스턴스를 파이썬 네이티브 데이터 유형(dict)으로 변환했다. 이를 직렬화를 통해 Json으로 랜더링 하자.
from rest_framework.renderers import JSONRenderer
json = JSONRenderer().render(serializer.data)
json
# b'{"email":"leila@example.com","content":"foo bar","created":"2016-01-27T15:17:10.375877"}'
3. Deserializing Objects
역직렬화도 직렬화와 비슷하다. 바이너리 데이터를 파이썬 네이티브 형태로 역직렬화 한다.
import io
from rest_framework.parsers import JSONParser
stream = io.BytesIO(json)
data = JSONParser().parse(stream)
그런 다음 원시 데이터 유형을 검증된 데이터(유효성 검사를 통과한)의 파이썬 데이터 타입으로 바꾼다.
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)}
4. Saving Instances
검증된 데이터를 기반으로 인스턴스를 반환하기 위해서는 create() 및 update() 메서드 중 하나 또는 둘 다를 구현해야 한다. 예를 들면
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
만약 개체 인스턴스가 Django 모델에 해당한다면 메서드에서 DB에 저장하는지 확인해야 한다.
def create(self, validated_data):
return Comment.objects.create(**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)
instance.save()
return instance
save()를 호출하면 기존 인스턴스가 전달되었는지 여부에 따라 새 인스턴스를 만들거나 기존 인스턴스를 업데이트한다.
# .save() will create a new instance.
serializer = CommentSerializer(data=data)
# .save() will update the existing `comment` instance.
serializer = CommentSerializer(comment, data=data)
4.1 인자 추가
또한 인자를 추가할 수도 있다. 현재 사용자, 현재 시간 등 다른 모든 정보가 포함될 수 있다.
serializer.save(owner=request.user)
이렇게 추가 키워드 인수는 create() 또는 update()에서 validated_data 인수에 포함되어 쓸 수 있다.
4.2 Overriding save() directly
경우에 따라 create() 및 update() 메서드가 의미가 없을 수 있다. 예를 들어 연락처 양식에서 새 인스턴스를 만드는 것이 아니라 전자 메일이나 다른 메시지를 보내는 것일 수 있다. 이러한 경우에는 읽기 쉽고 의미 있는 save()를 직접 재정의하도록 선택할 수 있다. 예를 들면
class ContactForm(serializers.Serializer):
email = serializers.EmailField()
message = serializers.CharField()
def save(self):
email = self.validated_data['email']
message = self.validated_data['message']
send_email(from=email, message=message)
이 경우 validated_data에 직접 접근해야 한다.
5. Validataion
데이터를 역직렬화할 때는 is_valid()를 호출하거나 개체 인스턴스를 저장한 뒤에 유효성이 확인된 데이터에 액세스 해야 한다. 유효성 검사에서 오류가 발생하면 오류 메시지가 출력된다.
serializer = CommentSerializer(data={'email': 'foobar', 'content': 'baz'})
serializer.is_valid()
# False
serializer.errors
# {'email': ['Enter a valid e-mail address.'], 'created': ['This field is required.']}
is_valid() 메서드는 raise_exception 플래그를 사용하여 HTTP 400 Bad Request 응답을 반환할 수 있다. 이러한 예외는 DRF가 제공하는 기본 예외 핸들러에 의해 자동으로 처리된다.
# Return a 400 response if the data was invalid.
serializer.is_valid(raise_exception=True)
5.1 Field-level validation
사용자 지정 필드 수준 검증을 지정할 수 있다. 메서드 이름을 validate_<field_name>으로 추가하면 된다. 해당 메서드는 유효한 값을 반환하거나 serializers.ValidationError를 반환해야 한다.
from rest_framework import serializers
class BlogPostSerializer(serializers.Serializer):
title = serializers.CharField(max_length=100)
content = serializers.CharField()
def validate_title(self, value):
"""
Check that the blog post is about Django.
"""
if 'django' not in value.lower():
raise serializers.ValidationError("Blog post is not about Django")
return value
만약 필드 변수가 required=False로 되어있다면 유효성 검사는 수행되지 않는다.
5.2 Object-level validation
여러 필드에 접근해서 유효성 검사를 수행하려면 validate() 메서드를 추가해라.
from rest_framework import serializers
class EventSerializer(serializers.Serializer):
description = serializers.CharField(max_length=100)
start = serializers.DateTimeField()
finish = serializers.DateTimeField()
def validate(self, data):
"""
Check that start is before finish.
"""
if data['start'] > data['finish']:
raise serializers.ValidationError("finish must occur after start")
return data
5.3 Validatiors
필드 인스턴스에 선언하여 validators를 포함시킬 수 있다.
def multiple_of_ten(value):
if value % 10 != 0:
raise serializers.ValidationError('Not a multiple of ten')
class GameRecord(serializers.Serializer):
score = IntegerField(validators=[multiple_of_ten])
...
또는 다음과 같이 메타 클래스에 선언함으로써 validators를 이용할 수 있다.
class EventSerializer(serializers.Serializer):
name = serializers.CharField()
room_number = serializers.IntegerField(choices=[101, 102, 103, 201])
date = serializers.DateField()
class Meta:
# Each room only has one event per day.
validators = [
UniqueTogetherValidator(
queryset=Event.objects.all(),
fields=['room_number', 'date']
)
]
validators에 대해 더 많이 알고 싶다면 이곳으로 가보자.
'Django' 카테고리의 다른 글
[Pytest] - TestRunner 설정하기 (0) | 2022.12.19 |
---|---|
[Pytest] - django-dotenv 와 pytest-dotenv 오류 (0) | 2022.12.19 |
[DRF - Django Rest Framework] Serializer Field 톺아보기(3) (0) | 2022.12.13 |
Django Queryset - Lazy QuerySet 과 올바른 Caching 사용법 (0) | 2022.12.09 |
[DRF - Django Rest Framework] Serializer Field 톺아보기(2) (0) | 2022.12.07 |