Serializer: 쿼리셋, 모델 인스턴스 등의 복잡한 데이터를 파이썬 데이터 타입으로 바꿔줌
예시로 사용할 오브젝트:
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')
Comment 오브젝트에 해당하는 데이터를 serialize/deserialize하는 데 사용할 serializer 선언
from rest_framework import serializers
class CommentSerializer(serializers.Serializer):
email = serializers.EmailField()
content = serializers.CharField(max_length=200)
created = serializers.DateTimeField()
위에서 정의한 CommentSerializer을 이용하여 댓글 또는 댓글 리스트 serialize하기
serilizer = CommentSerializer(comment)
serializer.data
# {'email': 'leila@example.com', 'content': 'foo bar', 'created': '2016-01-27T15:17:10.375877'}
데이터를 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"}'
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)}
검증된 데이터를 기반으로 완전한 오브젝트 인스턴스를 돌려주려면 .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
.save()를 추가하여 오브젝트를 디비에 저장
...
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
serializer.save(owner=request.user)
.save() 메서드를 호출해서 검증된 데이터에 기반한 object instance을 돌려줌
comment = serializer.save()
serializer 클래스를 정의할 때 존재하는 인스턴스가 전달됐는지 여부에 따라 .save() 메서드의 호출은 새로운 인스턴스를 생성하거나 존재하는 인스턴스를 업데이트함
# 새로운 인스턴스를 생성할 .save() 호출
serializer = CommentSerializer(data=data)
# 이미 존재하는 'comment' 인스턴스를 업데이트할 .save() 호출
serializer = CommentSerializer(comment, data=data)
데이터를 deserialize할 때에는 검증된 데이터를 접근하기 전에 무조건 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 플래그 옵션이 있음
# Return a 400 response if the data was invalid.
serializer.is_valid(raise_exception=True)
serializer의 각 필드는 validator를 포함할 수 있음
1. 필드 인스턴스에 직접 선언하기
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])
...
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']
)
]
serializer는 기본적으로 모든 필수 필드에 값을 받지 않으면 검증 에러를 발생 시킴
# Update `comment` with partial data
serializer = CommentSerializer(comment, data={'content': 'foo bar'}, partial=True)
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()
nested 프로필 오브젝트가 있는 유저를 생성하는 클래스
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
def 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 instance
User 인스턴스와 Profile 인스턴스를 항상 페어로 같이 생성하기:
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
오브젝트 리스트를 serialize/deserialize하기
쿼리셋 또는 오브젝트 리스트를 serialize하려면 serializer를 정의할 때 many=True 플래그를 전달해야 함
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'}
# ]
serializer = AccountSerializer(account, context={'request': request})
serializer.data
# {'id': 6, 'owner': 'denvercoder9', 'created': datetime.datetime(2013, 2, 12, 09, 44, 56, 678870), 'details': 'http://example.com/accounts/6/details'}
ModelSerializer
class AccountSerializer(serializers.ModelSerializer):
class Meta:
model = Account
fields = ['id', 'account_name', 'users', 'created']
fields 또는 exclude 옵션으로 model serialzer의 기본 필드 중 사용하고 싶은 것을 고를 수 있음
class AccountSerializer(serializers.ModelSerializer):
class Meta:
model = Account
fields = ['id', 'account_name', 'users', 'created']
모델의 모든 필드 사용하기:
class AccountSerializer(serializers.ModelSerializer):
class Meta:
model = Account
fields = '__all__'
특정 필드를 제외하고 모두 사용하기:
class AccountSerializer(serializers.ModelSerializer):
class Meta:
model = Account
exclude = ['users']
depth 옵션을 사용한 nested 표현
class AccountSerializer(serializers.ModelSerializer):
class Meta:
model = Account
fields = ['id', 'account_name', 'users', 'created']
depth = 1
ModelSerializer에 필드 추가하기 또는 기존 필드 override하기
class AccountSerializer(serializers.ModelSerializer):
url = serializers.CharField(source='get_absolute_url', read_only=True)
groups = serializers.PrimaryKeyRelatedField(many=True)
class Meta:
model = Account
fields = ['url', 'groups']
읽기만 가능한 필드 정의하기
read_only_fields 메타 옵션 정의하기
class AccountSerializer(serializers.ModelSerializer):
class Meta:
model = Account
fields = ['id', 'account_name', 'users', 'created']
read_only_fields = ['account_name']
필드에 랜덤한 키워드 인자 추가하기 > extra_kwargs 옵션
class CreateUserSerializer(serializers.ModelSerializer):
class Meta:
model = User
fields = ['email', 'username', 'password']
extra_kwargs = {'password': {'write_only': True}}
def create(self, validated_data):
user = User(
email=validated_data['email'],
username=validated_data['username']
)
user.set_password(validated_data['password'])
user.save()
return user
HyperlinkedModelSerializer
serializer = AccountSerializer(queryset, context={'request': request})
ListSerializer 클래스
BaseSerializer 클래스
class HighScore(models.Model):
created = models.DateTimeField(auto_now_add=True)
player_name = models.CharField(max_length=10)
score = models.IntegerField()
참고: django REST framework serializer 공식 문서