Serializers
Serializer란??
처음 듣는 용어. 직렬 변환기?
In computer science, in the context of data storage, serialization is the process of translating data structures or object state into a format that can be stored (for example, in a file or memory buffer) or transmitted (for example, across a network connection link) and reconstructed later (possibly in a different computer environment).[1]
serializers: 쿼리셋, 모델 인스턴스 등의 복잡한 데이터를 JSON, XML 등의 컨텐트 타입으로 쉽게 변환 가능한 python datatype으로 변환시켜줌
deserialization: 받은 데이터를 validating 한 후에 parsed data를 complex type으로 다시 변환
- Declaring Serializers - 선언
Comment object example
from datetime import datetime
class Comment(object):
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, deserialize 하기 위해 serializer를 선언, form 선언하는것과 비슷하게 serializer를 선언
from rest_framework import serializers
class CommentSerializer(serializers.Serializer):
email = serializers.EmailField()
content = serializers.CharField(max_length=200)
created = serializers.DateTimeField()
1. Serializing objects
위에서 만든 CommentSerializer를 통해 comment (list)를 serialize 할 수 있다. (form class 이용과 흡사하게)
serializer = CommentSerializer(comment)
serializer.data
# {'email': 'leila@example.com', 'content': 'foo bar', 'created': '2016-01-27T15:17:10.375877'}
=> 모델 인스턴스를 python native datatypes로 변환. 최종적으로 data를 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"}'
Deserializing objects
2.deserialization도 비슷하게.
from django.utils.six import BytesIO
from rest_framework.parsers import JSONParser
stream = BytesIO(json)
data = JSONParser().parse(stream)
validaed data를 저장
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)}
3. Saving instances
create()나 update() 메소드를 이용해서 object instance를 반환 가능. (Optional)
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
이후 data를 deserializing 할 때 save()를 호출해서 저장 가능. save 는 instance가 존재하면 update를, 그렇지 않으면 create를 해줌.
- save()에 추가적인 atrribute 사용 가능
serializer.save(owner=request.user)
- save()를 직접 overriding 가능
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)
Validation
4.data를 deserializing 할 때, instance를 저장하기 전항상 is_valid()
를 호출해야함
에러가 발생하면 .errors로 에러 메세지 호출 가능. {'field name': ['error message']}
형식으로.
Raise_exception
: is_valid() 메소드에 optional로 raise_exception
True일 경우 REST framework가 제공하는 기본 exception handler => 400에러 반환
# Return a 400 response if the data was invalid.
serializer.is_valid(raise_exception=True)
- Field-level validation: 각 field별로 validate 생성
.validate_<field_name>
형태로 필드별 validation 가능
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
- Object-level validation: object 전역에 validate
multiple 필드에 validation이 필요하면 subclass로 .validate() 사용,
data를 arg로 받음=> field value dictionary. validate해서 ValidationError를 반환하거나 data 반환
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 the start is before the stop.
"""
if data['start'] > data['finish']:
raise serializers.ValidationError("finish must occur after start")
return data
- validator: 따로 validator를 만들어서 field 정의시 삽입
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])
...
=> Meta에 넣어서 완성된 field data에 적용시킬수도 있음
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']
)
Accessing the initial data and instance
5.initial_data로 initial data 사용 가능. 없다면 None 반환
Partial updates
6.default로 모든 required fields를 넣어주지 않으면 validation error.
partial arg를 통해서 업데이트 가능
# Update `comment` with partial data
serializer = CommentSerializer(comment, data={'content': u'foo bar'}, partial=True)
Dealing with nested objects
7.다른 serializer class를 field로 받을 수 있음
class UserSerializer(serializers.Serializer):
email = serializers.EmailField()
username = serializers.CharField(max_length=100)
class CommentSerializer(serializers.Serializer):
user = UserSerializer()
content = serializers.CharField(max_length=200)
created = serializers.DateTimeField()
required=False, many=True 로 사용 가능
Writable nested representations
8.nested된 serializer data에서 error 발생시 nested된 field name으로 나옴. validated_data도 마찬가지
serializer = CommentSerializer(data={'user': {'email': 'foobar', 'username': 'doe'}, 'content': 'baz'})
serializer.is_valid()
# False
serializer.errors
# {'user': {'email': [u'Enter a valid e-mail address.']}, 'created': [u'This field is required.']}
class UserSerializer(serializers.ModelSerializer):
profile = ProfileSerializer()
class Meta:
model = User
fields = ('username', 'email', 'profile')
def create(self, validated_data):
profile_data = validated_data.pop('profile')
user = User.objects.create(**validated_data)
Profile.objects.create(user=user, **profile_data)
return user
- nested representation의 .update() medtod
update인 경우에는 조금 더 문제갸복잡하다. 만약에 관계가 있는 field 가 None일 경우에,
- relationship을 db에서 NULL처리?
- 연관된 istance를 삭제?
- 데이터를 무시하고 instance를 그대로 놔두기?
- validation error?
def update(self, instance, validated_data):
profile_data = validated_data.pop('profile')
# Unless the application properly enforces that this field is
# always set, the follow could raise a `DoesNotExist`, which
# would need to be handled.
profile = instance.profile
instance.username = validated_data.get('username', instance.username)
instance.email = validated_data.get('email', instance.email)
instance.save()
profile.is_premium_member = profile_data.get(
'is_premium_member',
profile.is_premium_member
)
profile.has_support_contract = profile_data.get(
'has_support_contract',
profile.has_support_contract
)
profile.save()
return instance
=> create, update가 모호하고 related model간에는 복잡한 의존도가 필요하기 때문에 REST에서는 항상 method를 명시적으로 사용 해야함.
- Handling saving related instance in model manager class
related instance를 여러 개 저장하는 다른 방법은 custom model manager를 사용하는 방법
class UserManager(models.Manager):
...
def create(self, username, email, is_premium_member=False, has_support_contract=False):
user = User(username=username, email=email)
user.save()
profile = Profile(
user=user,
is_premium_member=is_premium_member,
has_support_contract=has_support_contract
)
profile.save()
return user
위에서 create를 재정의 한 후에,
def create(self, validated_data):
return User.objects.create(
username=validated_data['username'],
email=validated_data['email']
is_premium_member=validated_data['profile']['is_premium_member']
has_support_contract=validated_data['profile']['has_support_contract']
)
이런 식으로 model manager에서 정의한 create를 호출해서 사용 가능.
자세한 내용은 django model manager 문서 참조
Dealing with multiple objects
9.serializer는 object들의 list도 serializing / deserializing 가능
- 여러 objects serializing
many=True
flag를 추가. 쿼리셋이나 리스트를 serializing 가능
queryset = Book.objects.all()
serializer = BookSerializer(queryset, many=True)
serializer.data
# [
# {'id': 0, 'title': 'The electric kool-aid acid test', 'author': 'Tom Wolfe'},
# {'id': 1, 'title': 'If this is a man', 'author': 'Primo Levi'},
# {'id': 2, 'title': 'The wind-up bird chronicle', 'author': 'Haruki Murakami'}
# ]
- deserializing
multiple create는 가능, update는 불가능. 아래 ListSerializer에서 자세히 설명
Including extra context
10.serializer를 처음 만들 때 context arg로 다른 context를 추가 가능하다.
serializer = AccountSerializer(account, context={'request': request})
serializer.data
# {'id': 6, 'owner': u'denvercoder9', 'created': datetime.datetime(2013, 2, 12, 09, 44, 56, 678870), 'details': 'http://example.com/accounts/6/details'}
'backend > django-rest-framework' 카테고리의 다른 글
django rest framework viewset router의 namespace (0) | 2018.01.11 |
---|