왜?
장고의 serializer는 잘만 활용하면, 효율적으로 valid 검사부터, 쿼리셋에서 불러오는것까지 간단히 구현가능하다. 자세히 알수록 활용하기 쉬워질것같다.
장고의 form과 비슷한점이 매우많다고 생각한다.
Serializer
직렬화를 하는 직렬변환기?
Serialize(직렬화)
쿼리셋,모델 인스턴스 등의 complex type(복잡한 데이터)를 JSON, XML등의 컨텐트 타입으로 쉽게 변환 가능한 python datatype으로 변환시켜줌
Serializer는 우리가 Django 에서 사용하는 파이썬 객체나 queryset 같은 복잡한 객체들을 REST API에서 사용할 json 과 같은 형태로 변환해주는 어댑터 역할을 한다.
Deserialize
받은 데이터(크롤링시 parse사용>python datatype)를 validating 한 후에 parsed data를 complex type으로 다시 변환
이때는 반드시 is_valid()를 호출하여 검사하자
serializer 구성해보기
DB와 DB인스턴스 생성 예시
1 | #DB |
serializer 생성, django form과 매우 흡사(is_valid()도 체크됨)
1 | #serializers.py |
serializer 사용해보기
serialize 해보기
- DB인스턴스를 직렬화하는것을 볼수있음
1 |
|
deserialize 해보기
- Parsing된 데이터(Python datatype)을 is_valid()해주고 추후 save()시에 qs로 가능
1 | import io |
Saving instances
create()나 update()메소드를 오버라이딩가능
1 | class CommentSerializer(serializers.Serializer): |
이후 data를 deserializing할떄 save()를 호출해서 저장 가능. save는 instance가 존재하면 update, 아니면 create 해줌
1
2
3
4
5 # .save() will create a new instance.
serializer = CommentSerializer(data=data)
# .save() will update the existing `comment` instance.
serializer = CommentSerializer(comment, data=data)
1 serializer.save()
save()에 추가적인 attribute 사용가능
serializer.save(owner=request.user)
save()를 직접 오버라이딩가능
1
2
3
4
5
6
7
8class 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
data를 deserializing할 때, instance.save()하기전에 반드시 is_valid()를 호출해야함. 에러발생시 .errors로 에러 메세지 호출가능
{'field name': ['error message']}
Raise_exception
는 is_valid()메소드에서 optional로 raise_exception=True일때,400에러 반환1
2# Return a 400 response if the data was invalid.
serializer.is_valid(raise_exception=True)Field-level validation: 각 필드별로 validate생성
1
2
3
4
5
6
7
8
9
10
11
12
13from 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 valueObject-level validation: object 전역에 validate
이건 멀티 필드에 대해 validation필요할때 .validate()사용
1
2
3
4
5
6
7
8
9
10
11
12
13
14from 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 datavalidator: 따로 validator를 만들어 field정의시
1
2
3
4
5
6
7def 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에 적용시킬수도있다
1
2
3
4
5
6
7
8
9
10
11
12
13class 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
Partial updates
default로 모든 required fields를 넣어주지 않으면 validation error.
partial arg를 통해서 업데이트 가능
1 | # Update `comment` with partial data |
Dealing with nested objects
다른 serializer class를 field로 받을 수 있음
1 | class UserSerializer(serializers.Serializer): |
required=False, many=True로 사용가능
Writable nested representations
nested된 serializer data에서 error발생시 nested된 field name으로 나옴.
validated_data도 마찬가지
1 | serializer = CommentSerializer(data={'user': {'email': 'foobar', 'username': 'doe'}, 'content': 'baz'}) |
nested_data를 처리하는 방법을 써놓음(???)
create: profile에 해당하는 데이터를 pop으로 뺴고, 해당하는 다른 DB에 저장하는게 핵심
1
2
3
4
5
6
7
8
9
10
11
12class 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 userupdate: 복잡하다. 만약에 관계가 있는 field가 None일 경우
- relationship을 DB에서 NULL처리?
- 연관된 instance을 삭제?
- 데이터를 무시하고 instance를 그래도 놔두기?
- validation Error
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22def update(self, instance, validated_data):
profile_data = validated_data.pop('profile')
# Unless the application properly enforces that this field is
# always set, the following 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 instancecreate, update가 모호하고 related model간에서 복잡한 의존도가 필요하기 때문에 REST에서는 항상 method를 명시적으로 사용해야함
Handling saving related instance in model manager class
related instance를 여러개 저장하는 다른 방법은 custom model manager를사용하는 방법
1 | class UserManager(models.Manager): |
위에서 create를 재정이 한후에
1 | def create(self, validated_data): |
이런식으로 model manager에서 정의한 create를 호출해서 사용가능
자세한 내용은 django model manager문서 보자
Dealing with mutiple objects
serializer는 object들의 list도 serializing/deserializing 가능
여러 objects serializing
many=True flag를 추가, 퀴리셋이나 리스트를 serializing가능
1
2
3
4
5
6
7
8queryset = 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'}
# ]
여러 objects deserializing
- multiple create는 가능, update는 불가능, 아래 ListSerializer에서 자세히 설명
Including extra context
serializer를 처음 만들 때 context arg로 다른 context를 추가 가능하다.
1 | serializer = AccountSerializer(account, context={'request': request}) |