[DRF] 공식 문서 - serializers 정리
1. Serializers
1) 정의
- Serializer는 쿼리셋이나 모델 인스턴스와 같은 복잡한 데이터를 JSON, XML 등의 콘텐츠 타입으로 쉽게 변환할 수 있는 원시 파이썬 데이터 타입으로 바꿔주는 역할을 한다.
- 또한 Serializer는 역직렬화도 제공하여, 들어온 데이터를 검증한 후에 복잡한 데이터 타입으로 다시 변환할 수 있게 해준다.
- REST 프레임워크의 Serializer는 Django의 Form과 ModelForm 클래스와 매우 비슷하게 동작한다.
- 우리는 응답의 출력을 제어하는 강력하면서도 범용적인 방법을 제공하는 Serializer 클래스를 제공하고 있다.
- 또한, 모델 인스턴스와 쿼리셋을 다루는 Serializer를 쉽게 생성할 수 있도록 ModelSerializer 클래스를 제공한다.
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')
- Comment 객체에 해당하는 데이터를 직렬화하고 역직렬화하는 데 사용할 수 있는 Serializer를 선언해 보자.
- Serializer를 선언하는 것은 Form을 선언하는 것과 매우 유사하다:
from rest_framework import serializers
class CommentSerializer(serializers.Serializer):
email = serializers.EmailField()
content = serializers.CharField(max_length=200)
created = serializers.DateTimeField()
2. Serializing objects
- CommentSerializer를 사용하여 댓글이나 댓글 목록을 직렬화할 수 있다.
- 다시 말해, Serializer 클래스를 사용하는 것은 Form 클래스를 사용하는 것과 많이 비슷하다.
serializer = CommentSerializer(comment)
serializer.data
# {'email': 'leila@example.com', 'content': 'foo bar', 'created': '2016-01-27T15:17:10.375877'}
- 현재 시점에서 모델 인스턴스를 Python 기본 데이터 유형으로 변환했다.
- 직렬화 프로세스를 완료하기 위해 데이터를 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
- 역직렬화도 유사한 방식으로 진행된다.
- 먼저 스트림을 파싱하여 Python 기본 데이터 유형으로 변환한다.
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 모델에 해당한다면, 이러한 메서드가 객체를 데이터베이스에 저장되도록 해야 한다.
- 예를 들어, Comment가 Django 모델이라면, 메서드는 다음과 같을 수 있다.
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()를 호출해 객체 인스턴스를 반환할 수 있다.
- .update()를 사용해 코드를 줄일 수 있다.
def update(self, instance, validated_data):
instance.update(**validated_data)
return instance
- 데이터를 역직렬화할 때 유효성을 검사한 후, .save() 메서드를 호출하여 해당 데이터를 기반으로 객체 인스턴스를 반환할 수 있다.
- 이를 통해 검증된 데이터를 사용하여 객체 인스턴스를 생성하고, 필요한 경우 해당 인스턴스를 데이터베이스에 저장할 수 있다.
comment = serializer.save()
- .save()를 호출하면, 직렬화 클래스를 인스턴스화할 때 기존 인스턴스가 전달되었는지에 따라 새로운 인스턴스를 생성하거나 기존 인스턴스를 업데이트한다.
- .create() 메서드와 .update() 메서드는 모두 선택사항이다.
- serializer class의 사용 사례에 따라 두 메서드 중 하나, 둘 다, 또는 하나도 구현할 필요가 없다.
# .save() will create a new instance.
serializer = CommentSerializer(data=data)
# .save() will update the existing `comment` instance.
serializer = CommentSerializer(comment, data=data)
1) .save() 메서드에 추가 속성을 전달하기
- 때로는 인스턴스를 저장하는 시점에서 뷰 코드에서 추가 데이터를 주입하고 싶을 수 있다.
- 이 추가 데이터에는 현재 사용자, 현재 시간 또는 요청 데이터에 포함되지 않은 기타 정보와 같은 내용이 포함될 수 있다.
- .save()를 호출할 때 추가 키워드 인자를 포함하여 이를 수행할 수 있다.
- .create() 또는 .update()가 호출될 때, 모든 추가 키워드 인자는 validated_data 인자에 포함된다.
serializer.save(owner=request.user)
2) .save()를 직접 오버라이딩하기
- 일부 경우에는 .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)
- 위의 경우에는 이제 직접 serializer의 .validated_data 속성에 접근해야 한다.
5. Validation
- 데이터를 역직렬화할 때는 항상 .is_valid()를 호출한 후에 유효한 데이터에 접근하거나 객체 인스턴스를 저장해야 한다.
- 유효성 검사 중에 오류가 발생하면 .errors 속성에 결과 오류 메시지를 나타내는 딕셔너리가 포함된다.
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.']}
- 해당 딕셔너리의 각 키는 필드 이름이며, 값은 해당 필드에 대한 오류 메시지의 문자열 목록이다.
- non_field_errors 키도 존재할 수 있으며, 일반적인 유효성 검사 오류를 나열한다.
- non_field_errors 키의 이름은 NON_FIELD_ERRORS_KEY REST framework 설정을 사용하여 사용자 정의할 수도 있다.
from rest_framework import serializers
class MySerializer(serializers.Serializer):
class Meta:
non_field_errors_key = 'custom_non_field_errors'
def validate(self, data):
# 유효성 검사를 수행하고 오류가 발생하면 custom_non_field_errors에 오류 메시지를 추가합니다.
if not data.get('some_field'):
self.errors[self.Meta.non_field_errors_key] = ['some_field는 필수입니다.']
return data
- 혹은 아래와 같이 사용이 가능하다.
REST_FRAMEWORK = {
'NON_FIELD_ERRORS_KEY': 'custom_non_field_errors'
}
self.errors['custom_non_field_errors'] = ['일반적인 유효성 검사 오류입니다.']
- 항목 목록을 역직렬화할 때, 오류는 각각의 역직렬화된 항목을 나타내는 딕셔너리의 목록으로 반환된다.
1) 잘못된 데이터에 대해 예외 발생
- .is_valid() 메서드는 선택사항으로 raise_exception 플래그를 사용할 수 있으며, 유효성 검사 오류가 있는 경우 serializers.ValidationError 예외를 발생시킨다.
- 이러한 예외는 REST 프레임워크가 제공하는 기본 예외 처리기에 의해 자동으로 처리되며, 기본적으로 HTTP 400 Bad Request 응답을 반환한다.
# Return a 400 response if the data was invalid.
serializer.is_valid(raise_exception=True)
2) 필드 수준의 유효성 검사
- Serializer 하위 클래스에 .validate_<field_name> 메서드를 추가함으로써 사용자 정의 필드 수준의 유효성 검사를 지정할 수 있다.
- 이는 Django 폼의 .clean_<field_name> 메서드와 유사하다.
- 이 메서드들은 유효성 검사가 필요한 필드 값인 단일 인자를 받는다.
- 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
- 참고: 만약 <field_name>이 필드가 필수가 아닌 매개변수 required=False로 선언된 경우, 해당 필드가 포함되지 않으면 이 유효성 검사 단계는 수행되지 않는다.
3) 객체 수준의 유효성 검사
- 여러 필드에 접근이 필요한 다른 유효성 검사를 수행하려면 Serializer 하위 클래스에 .validate()라는 메서드를 추가하면 된다.
- 이 메서드는 필드 값들로 이루어진 딕셔너리를 단일 인자로 받는다.
- 필요한 경우 serializers.ValidationError을 발생시키거나 유효성이 검증된 값을 반환해야 한다.
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
4) 유효성 검사자
- 시리얼라이저의 개별 필드는 필드 인스턴스에서 선언하여 유효성 검사자(Validators)를 포함할 수 있다.
def multiple_of_ten(value):
if value % 10 != 0:
raise serializers.ValidationError('Not a multiple of ten')
class GameRecord(serializers.Serializer):
score = serializers.IntegerField(validators=[multiple_of_ten])
...
- 시리얼라이저 클래스는 필드 데이터 전체에 적용되는 재사용 가능한 유효성 검사자(Validators)를 포함할 수 있다.
- 이러한 유효성 검사자는 inner Meta 클래스에서 선언함으로써 포함된다.
- 예를 들면 다음과 같다:
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) 문서를 참조
- https://www.django-rest-framework.org/api-guide/validators/
6. 초기 데이터와 인스턴스에 접근하는 방법
- 시리얼라이저 인스턴스에 초기 객체 또는 쿼리셋을 전달할 때, 해당 객체는 .instance 속성으로 사용할 수 있다.
- 초기 객체가 전달되지 않은 경우 .instance 속성은 None이 된다.
- 시리얼라이저 인스턴스에 데이터를 전달할 때, 수정되지 않은 데이터는 .initial_data로 사용할 수 있다.
- 데이터 키워드 인자가 전달되지 않은 경우 .initial_data 속성은 존재하지 않을 것이다.
7. 부분 업데이트
- 기본적으로, 시리얼라이저는 모든 필수 필드에 대한 값을 전달받아야 하며, 그렇지 않을 경우 유효성 검사 오류가 발생한다.
- 부분 업데이트를 허용하려면 partial 인자를 사용할 수 있다.
# Update `comment` with partial data
serializer = CommentSerializer(comment, data={'content': 'foo bar'}, partial=True)
8. 중첩된 개체 다루기
- 이전 예시는 문자열, 날짜 또는 정수와 같은 간단한 데이터 유형만 가진 객체를 다루는 데에는 적합하다.
- 그러나 때로는 더 복잡한 객체를 표현해야 하는 경우도 있다.
- 이 경우 객체의 일부 속성이 문자열, 날짜 또는 정수와 같은 간단한 데이터 유형이 아닐 수 있다.
- Serializer 클래스는 자체적으로 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()
- 중첩된 표현이 None 값을 선택적으로 허용할 수 있는 경우, required=False 플래그를 중첩된 serializer에 전달해야 한다.
class CommentSerializer(serializers.Serializer):
user = UserSerializer(required=False) # May be an anonymous user.
content = serializers.CharField(max_length=200)
created = serializers.DateTimeField()
- 마찬가지로 중첩된 표현이 항목들의 목록이어야 하는 경우, 중첩된 serializer에 many=True 플래그를 전달해야 한다.
class CommentSerializer(serializers.Serializer):
user = UserSerializer(required=False)
edits = EditItemSerializer(many=True) # A nested list of 'edit' items.
content = serializers.CharField(max_length=200)
created = serializers.DateTimeField()
9. 쓰기 가능한 중첩 표현
- 중첩 표현을 다룰 때 데이터를 역직렬화 지원하는 경우, 중첩된 객체와 관련된 모든 오류는 중첩된 객체의 필드 이름 아래에 중첩된다.
serializer = CommentSerializer(data={'user': {'email': 'foobar', 'username': 'doe'}, 'content': 'baz'})
serializer.is_valid()
# False
serializer.errors
# {'user': {'email': ['Enter a valid e-mail address.']}, 'created': ['This field is required.']}
- 유사한 것으로, .validated_data 속성은 중첩된 데이터 구조를 포함하게 된다..
if serializer.is_valid():
# 유효성 검사를 통과한 데이터를 확인할 수 있음
print(serializer.validated_data)
1) 중첩 표현에 대한 .create() 메소드 작성
- 쓰기 가능한 중첩 표현을 지원한다면, 여러 객체를 저장하는 것을 처리하는 .create() 또는 .update() 메소드를 작성해야 한다.
- 다음 예시는 중첩된 프로필 객체를 가진 사용자를 생성하는 방법을 보여준다.
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
2) 중첩 표현에 대한 .update() 메소드 작성
- 업데이트의 경우, 관계에 대한 업데이트를 어떻게 처리할지 신중하게 고려해야 한다.
- 예를 들어, 관계 데이터가 None이거나 제공되지 않은 경우, 다음 중 어느 것이 발생해야 할까?
1.데이터베이스에서 관계를 NULL로 설정한다.
2. 관련 인스턴스를 삭제한다.
3. 데이터를 무시하고 인스턴스를 그대로 둔다.
4. 유효성 검사 오류를 발생시킨다.
- 다음은 이전에 언급했던 UserSerializer 클래스에 대한 .update() 메소드에 대한 예시이다.
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
- 중첩된 생성과 업데이트의 동작이 모호할 수 있고, 관련 모델 간에 복잡한 의존성을 요구할 수 있기 때문에, REST 프레임워크 3 에서는 이러한 메소드를 항상 명시적으로 작성하도록 요구한다.
- 기본 ModelSerializer의 .create()와 .update() 메소드는 쓰기 가능한 중첩 표현을 지원하지 않는다. 때문에 중첩된 데이터 구조를 다루려면 사용자가 직접 .create()와 .upcate() 메소드를 오버라이딩해야 한다.
- 그러나 DRF Writable Nested 같은 쓰기 가능한 중첩 표현을 자동으로 지원하는 서드파티 패키지들이 있다.
3) 모델 매니저 클래스에서 관련 인스턴스 저장 처리
- Serializer에서 여러 관련 인스턴스를 저장하는 대안으로, 올바른 인스턴스를 생성하는 것을 처리하는 사용자 정의 모델 매니저 클래스를 작성할 수 있다.
- 예를 들어, 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
- 이 매니저 클래스는 사용자 인스턴스와 프로필 인스턴스가 항상 동시에 생성된다는 것을 더욱 잘 캡슐화한다.
- Serializer 클래스의 .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']
)
- 이 접근법에 대한 더 자세한 내용은 Django 문서의 모델 매니저 부분과 모델 및 매니저 클래스 사용에 대한 이 블로그 포스트를 참조하면 된다.
- model managers : https://docs.djangoproject.com/en/5.0/topics/db/managers/
- this blogpost on using model and manager classes : https://www.dabapps.com/insights/django-models-and-encapsulation/
10. 여러 객체 다루기
- 추가 컨텍스트 포함하기Serializer 클래스는 객체의 리스트를 직렬화하거나 역직렬화하는 것도 처리할 수 있다.
1) 여러 객체 직렬화하기
- 단일 객체 인스턴스 대신 쿼리셋 또는 객체 리스트를 직렬화하려면, 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'}
# ]
2) 여러 객체 역직렬화하기
- 여러 객체를 역직렬화하는 기본 동작은 여러 객체 생성을 지원하지만, 여러 객체 업데이트는 지원하지 않는다.
- 이러한 경우를 지원하거나 사용자 정의하는 방법에 대한 자세한 정보는 아래의 ListSerializer 문서를 참조하면 된다.
- https://www.django-rest-framework.org/api-guide/serializers/#listserializer
11. 추가 컨텍스트 포함하기
- 직렬화되는 객체 외에도 Serializer에게 추가 컨텍스트를 제공해야 하는 경우가 있다.
- 하나의 일반적인 경우는 하이퍼링크된 관계를 포함하는 Serializer를 사용하는 경우로, 이 경우 Serializer는 완전히 자격을 갖춘 URL을 적절히 생성할 수 있도록 현재 요청에 액세스해야 한다.
- Serializer를 인스턴스화할 때 context 인수를 전달함으로써 임의의 추가 컨텍스트를 제공할 수 있다.
- 예를 들면 다음과 같다.
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'}
- context 딕셔너리는 self.context 속성에 접근함으로써 사용자 정의 .to_representation() 메소드 등의 어떤 Serializer 필드 로직에서도 사용될 수 있다.
from rest_framework import serializers
class UserSerializer(serializers.ModelSerializer):
class Meta:
model = User
fields = ['username', 'email']
def to_representation(self, instance):
representation = super().to_representation(instance)
# self.context에서 추가 정보를 가져옵니다.
extra_data = self.context.get('extra_data')
# 추가 정보가 있는 경우, 반환할 데이터에 추가합니다.
if extra_data:
representation['extra_data'] = extra_data
return representation
- 참고 :
https://velog.io/@arara90/django-torepresentation
2. ModelSerializer
- 대부분의 경우, Django 모델 정의와 밀접하게 연관된 Serializer 클래스가 필요할 것이다.
- ModelSerializer 클래스는 모델 필드에 대응하는 필드를 가진 Serializer 클래스를 자동으로 생성하는 간편한 방법을 제공한다.
- ModelSerializer 클래스는 일반 Serializer 클래스와 동일하지만, 다음과 같은 차이점이 있다.
1) 모델에 기반하여 자동으로 일련의 필드를 생성해준다.
2) unique_together와 같은 Serializer에 대한 검증자를 자동으로 생성해준다.
3) create()와 .update()에 대한 간단한 기본 구현을 포함하고 있다.
- ModelSerializer를 선언하는 방법은 다음과 같다.
class AccountSerializer(serializers.ModelSerializer):
class Meta:
model = Account
fields = ['id', 'account_name', 'users', 'created']
- 기본적으로, 클래스에 있는 모든 모델 필드는 해당하는 Serializer 필드에 매핑된다.
- 모델에 있는 외래키와 같은 관계는 PrimaryKeyRelatedField에 매핑된다.
- 역방향 관계는 serializer 관계 문서에 명시적으로 포함되지 않는 한 기본적으로 포함되지 않는다.
1) ModelSerializer 검사하기
- Serializer 클래스는 그들의 필드 상태를 완전히 검사할 수 있도록 도움이 되는 자세한 표현 문자열을 생성한다.
- 이는 ModelSerializers를 사용할 때, 어떤 필드와 검증자 세트가 자동으로 생성되는지 확인하고자 할 때 유용하다.
- 이를 수행하려면, Django shell을 열고, python manage.py shell를 사용한 후, serializer 클래스를 import하고, 인스턴스화하고, 객체 표현을 출력하면 된다.
>>> from myapp.serializers import AccountSerializer
>>> serializer = AccountSerializer()
>>> print(repr(serializer))
AccountSerializer():
id = IntegerField(label='ID', read_only=True)
name = CharField(allow_blank=True, max_length=100, required=False)
owner = PrimaryKeyRelatedField(queryset=User.objects.all())
2) 포함할 필드 지정하기
- 만약 모델 Serializer에서 기본 필드의 일부만을 사용하고 싶다면, ModelForm에서 사용하는 것처럼 fields 또는 exclude 옵션을 사용하여 이를 수행할 수 있다.
- 모델이 변화할 때 데이터가 실수로 노출될 확률을 줄이기 위해, 직렬화할 필드는 모두 fields 속성을 사용하여 명시적으로 설정하는 것이 강력히 추천된다.
class AccountSerializer(serializers.ModelSerializer):
class Meta:
model = Account
fields = ['id', 'account_name', 'users', 'created']
- 또한, 모델의 모든 필드를 사용해야 함을 나타내기 위해 fields 속성을 특별한 값 'all'로 설정할 수도 있다.
class AccountSerializer(serializers.ModelSerializer):
class Meta:
model = Account
fields = '__all__'
- Serializer에서 제외할 필드의 목록을 exclude 속성에 설정할 수 있다.
class AccountSerializer(serializers.ModelSerializer):
class Meta:
model = Account
exclude = ['users']
- 위의 예시에서, 만약 Account 모델이 account_name, users, created 세 개의 필드를 가지고 있다면, 이는 account_name과 created 필드가 직렬화됨을 의미한다.
- fields와 exclude 속성의 이름은 일반적으로 모델 클래스의 모델 필드에 매핑된다.
- 또는 fields 옵션의 이름은 모델 클래스에 존재하는 인자가 없는 속성이나 메소드에 매핑될 수 있다.
class ExampleModel(models.Model):
field1 = models.CharField(max_length=100)
field2 = models.CharField(max_length=100)
@property
def combined_field(self):
return self.field1 + self.field2
def method_field(self):
return "Some calculation or logic"
class ExampleModelSerializer(serializers.ModelSerializer):
class Meta:
model = ExampleModel
fields = ['field1', 'field2', 'combined_field', 'method_field']
- 버전 3.3.0부터는 fields 또는 exclude 속성 중 하나를 제공하는 것이 필수적이다.
3) 중첩된 직렬화 지정하기
- 기본 ModelSerializer는 관계에 대해 기본 키를 사용하지만, depth 옵션을 사용하여 중첩된 표현을 쉽게 생성할 수도 있다.
class AccountSerializer(serializers.ModelSerializer):
class Meta:
model = Account
fields = ['id', 'account_name', 'users', 'created']
depth = 1
- depth 옵션은 플랫한 표현으로 전환하기 전에 탐색되어야 하는 관계의 깊이를 나타내는 정수 값을 설정해야 한다.
- 직렬화가 수행되는 방식을 사용자가 정의하고 싶다면, 필드를 직접 설정해야 한다.
4) 필드를 명시적으로 지정하기
- Serializer 클래스에서와 마찬가지로, 클래스에 필드를 선언함으로써 ModelSerializer에 추가 필드를 추가하거나 기본 필드를 재정의할 수 있다.
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']
- 추가 필드는 모델의 어떤 속성이나 호출 가능한 메소드에 대응될 수 있다.
5) 읽기 전용 필드 지정하기
- 여러 필드를 읽기 전용으로 지정하고 싶을 수 있다.
- 각 필드를 read_only=True 속성으로 명시적으로 추가하는 대신, read_only_fields라는 Meta 옵션을 사용할 수 있다.\
- 이 옵션은 필드 이름의 목록이나 튜플이어야 하며, 다음과 같이 선언된다:
class AccountSerializer(serializers.ModelSerializer):
class Meta:
model = Account
fields = ['id', 'account_name', 'users', 'created']
read_only_fields = ['account_name']
- editable=False가 설정된 모델 필드와 AutoField 필드는 기본적으로 읽기 전용으로 설정되므로, read_only_fields 옵션에 추가할 필요가 없습니다.
from django.db import models
class ExampleModel(models.Model):
id = models.AutoField(primary_key=True)
non_editable_field = models.CharField(max_length=100, editable=False)
editable_field = models.CharField(max_length=100)
from rest_framework import serializers
class ExampleModelSerializer(serializers.ModelSerializer):
class Meta:
model = ExampleModel
fields = ['id', 'non_editable_field', 'editable_field']
- 이 경우, read_only_fields 옵션을 명시적으로 지정하지 않아도 id 필드와 non_editable_field 필드는 읽기 전용으로 동작하며 이 필드들에 대한 수정 요청은 무시되거나 오류를 발생시킨다.
- 참고: 모델 수준에서 unique_together 제약 조건의 일부인 읽기 전용 필드가 특별한 경우가 있다.
- 이 경우, 필드는 제약 조건을 검증하기 위해 Serializer 클래스에 의해 필요하지만, 사용자가 수정할 수 없어야 한다.
- 이를 처리하는 적절한 방법은 Serializer에 필드를 명시적으로 지정하고, read_only=True와 default=… 키워드 인수를 모두 제공하는 것이다.
- 이의 한 예는 현재 인증된 User와 unique_together인 다른 식별자에 대한 읽기 전용 관계이다.
- 이 경우, user 필드를 다음과 같이 선언하면 된다.
user = serializers.PrimaryKeyRelatedField(read_only=True, default=serializers.CurrentUserDefault())
- ex )
from django.contrib.auth.models import User
from django.db import models
class ExampleModel(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE)
identifier = models.CharField(max_length=100)
class Meta:
unique_together = ('user', 'identifier')
- 이 경우 user와 identifier 필드가 unique_together 제약 조건을 형성하고 있다.
- 이 모델에 대한 ModelSerializer를 작성하면서 user 필드를 읽기 전용으로 만들고, 현재 인증된 사용자를 기본값으로 설정하려면 다음과 같이 작성할 수 있다.
from rest_framework import serializers
from rest_framework.validators import UniqueTogetherValidator
class ExampleModelSerializer(serializers.ModelSerializer):
user = serializers.HiddenField(
default=serializers.CurrentUserDefault()
)
class Meta:
model = ExampleModel
fields = ['user', 'identifier']
validators = [
UniqueTogetherValidator(
queryset=ExampleModel.objects.all(),
fields=('user', 'identifier')
)
]
- 이 경우 user 필드는 HiddenField를 사용하여 명시적으로 선언되었고, default 인자로 CurrentUserDefault()를 사용하여 현재 인증된 사용자를 기본값으로 설정했다.
- read_only=True는 HiddenField에 내장되어 있으므로 별도로 지정하지 않아도 된다.
- UniqueTogetherValidator는 user와 identifier 필드의 조합이 고유하도록 검증한다.
- UniqueTogetherValidator와 CurrentUserDefault 클래스에 대한 세부 정보는 Validators Documentation을 참조
- Validators : https://www.django-rest-framework.org/api-guide/validators/
- UniqueTogetherValidator : https://www.django-rest-framework.org/api-guide/validators/#uniquetogethervalidator
- CurrentUserDefault : https://www.django-rest-framework.org/api-guide/validators/#currentuserdefault
6) 추가 키워드 인수
- 또한, extra_kwargs 옵션을 이용하면 필드에 임의의 추가 키워드 인수를 지정할 수 있는 단축 방법이 있다.
- read_only_fields의 경우처럼 이 방법을 사용하면 직접 필드를 선언하지 않아도 된다.
- 이 옵션은 필드 이름을 키워드 인수의 딕셔너리에 매핑하는 딕셔너리이다.
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
- 필드가 이미 시리얼라이저 클래스에 명시적으로 선언되어 있다면, extra_kwargs 옵션은 무시된다.
7) 관계형 필드
- 모델 인스턴스를 직렬화할 때, 관계를 표현하는 데에는 여러 가지 방법을 선택할 수 있다.
- ModelSerializer의 기본 표현 방식은 관련 인스턴스의 기본 키를 사용하는 것이다.
- 다른 표현 방식으로는 하이퍼링크를 사용한 직렬화, 완전한 중첩 표현을 사용한 직렬화, 또는 사용자 정의 표현을 사용한 직렬화 등이 있다.
- 자세한 내용은 시리얼라이저 관계 문서를 참조
- https://www.django-rest-framework.org/api-guide/relations/
8) 필드 매핑 커스터마이징
- ModelSerializer 클래스는 시리얼라이저를 인스턴스화할 때 어떻게 시리얼라이저 필드가 자동으로 결정되는지 변경하기 위해 오버라이드할 수 있는 API도 제공한다.
- 일반적으로 ModelSerializer가 기본적으로 필요한 필드를 생성하지 않는 경우, 해당 필드를 클래스에 명시적으로 추가하거나, 단순히 일반 Serializer 클래스를 사용해야 한다.
- 그러나 경우에 따라서는 주어진 모델에 대해 시리얼라이저 필드가 어떻게 생성되는지 정의하는 새로운 기본 클래스를 생성 할 수도 있다.
1. serializer_field_mapping
- Django 모델 필드를 REST 프레임워크 시리얼라이저 필드에 매핑한다.
- 이 매핑을 오버라이드하여 각 모델 필드에 사용되어야 하는 기본 시리얼라이저 필드를 변경할 수 있습니다.
from django.db import models
from rest_framework import serializers
class CustomModelSerializer(serializers.ModelSerializer):
serializer_field_mapping = {
**serializers.ModelSerializer.serializer_field_mapping,
models.TextField: serializers.CharField,
models.DateTimeField: serializers.DateField,
}
class MyModel(models.Model):
text_field = models.TextField()
date_field = models.DateTimeField()
class MyModelSerializer(CustomModelSerializer):
class Meta:
model = MyModel
fields = ['text_field', 'date_field']
- 위 코드에서 CustomModelSerializer는 ModelSerializer의 serializer_field_mapping을 오버라이드하여, TextField는 CharField로, DateTimeField는 DateField로 매핑하도록 변경하였다.
- 따라서 MyModelSerializer를 사용하여 MyModel의 인스턴스를 직렬화하면, text_field와 date_field는 각각 CharField와 DateField의 직렬화 규칙에 따라 직렬화된다.
2. serializer_related_field
- 이 속성은 기본적으로 관계형 필드에 사용되는 시리얼라이저 필드 클래스여야 한다.
- ModelSerializer의 경우, 기본값은 serializers.PrimaryKeyRelatedField이다.
- HyperlinkedModelSerializer의 경우, 기본값은 serializers.HyperlinkedRelatedField이다.
from rest_framework import serializers
class CustomModelSerializer(serializers.ModelSerializer):
serializer_related_field = serializers.StringRelatedField
class MyModel(models.Model):
related_field = models.ForeignKey(OtherModel, on_delete=models.CASCADE)
class MyModelSerializer(CustomModelSerializer):
class Meta:
model = MyModel
fields = ['related_field']
- 위 코드에서 CustomModelSerializer는 serializer_related_field를 StringRelatedField로 설정하였다.
- StringRelatedField는 관계형 필드를 해당 객체의 문자열 표현으로 직렬화 한다.
- 따라서 MyModelSerializer를 사용하여 MyModel의 인스턴스를 직렬화하면, related_field 필드는 StringRelatedField에 따라 문자열 형태로 직렬화된다.
- 이는 기본적으로 설정된 PrimaryKeyRelatedField나 HyperlinkedRelatedField 대신 사용된다.
- 이와 같이 serializer_related_field를 사용하면 관계형 필드의 직렬화 방식을 커스터마이징할 수 있다.
3. serializer_url_field
- 시리얼라이저의 어떤 URL 필드에 사용되는 시리얼라이저 필드 클래스이다.
- 기본값은 serializers.HyperlinkedIdentityField이다.
4. serializer_choice_field
- 시리얼라이저의 어떤 선택 필드에 사용되는 시리얼라이저 필드 클래스이다.
- 기본값은 serializers.ChoiceField이다.
from rest_framework import serializers
class CustomModelSerializer(serializers.ModelSerializer):
serializer_choice_field = serializers.ChoiceField
class MyModel(models.Model):
COLOR_CHOICES = [
('R', 'Red'),
('B', 'Blue'),
('G', 'Green'),
]
color = models.CharField(choices=COLOR_CHOICES, max_length=1)
class MyModelSerializer(CustomModelSerializer):
class Meta:
model = MyModel
fields = ['color']
1) The field_class and field_kwargs API
- 다음 메소드들은 시리얼라이저에 자동으로 포함되어야 하는 각 필드에 대한 클래스와 키워드 인수를 결정하기 위해 호출된다.
- 이들 각각의 메소드는 (field_class, field_kwargs)의 두 튜플을 반환해야 한다.
- build_standard_field(self, field_name, model_field)
- 표준 모델 필드에 매핑되는 시리얼라이저 필드를 생성하기 위해 호출된다.
- 기본 구현은 serializer_field_mapping 속성에 기반한 시리얼라이저 클래스를 반환한다.
from rest_framework import serializers
class CustomModelSerializer(serializers.ModelSerializer):
def build_standard_field(self, field_name, model_field):
field_class, field_kwargs = super().build_standard_field(field_name, model_field)
# 여기에서 필드 클래스나 필드 키워드 인수를 커스터마이징할 수 있습니다.
return field_class, field_kwargs
class MyModel(models.Model):
name = models.CharField(max_length=100)
class MyModelSerializer(CustomModelSerializer):
class Meta:
model = MyModel
fields = ['name']
- 위 코드에서 CustomModelSerializer는 build_standard_field 메서드를 재정의하였다.
- 이 메서드는 표준 모델 필드에 매핑되는 시리얼라이저 필드를 생성하는 데 사용된다.
- super().build_standard_field(field_name, model_field)을 호출하여 기본 구현을 가져온 후, 필요에 따라 필드 클래스나 필드 키워드 인수를 커스터마이징할 수 있다.
- 이렇게 하면 MyModelSerializer를 사용하여 MyModel의 인스턴스를 직렬화할 때, name 필드는 커스터마이징된 방식에 따라 직렬화된다.
- build_relational_field(self, field_name, relation_info)
- 관계형 모델 필드에 매핑되는 시리얼라이저 필드를 생성하기 위해 호출된다.
- 기본 구현은 serializer_related_field 속성에 기반한 시리얼라이저 클래스를 반환한다.
- relation_info 인수는 model_field, related_model, to_many, has_through_model 속성을 포함하는 튜플이다.
from rest_framework import serializers
from django.db import models
class CustomModelSerializer(serializers.ModelSerializer):
def build_relational_field(self, field_name, relation_info):
field_class, field_kwargs = super().build_relational_field(field_name, relation_info)
# 여기에서 필드 클래스나 필드 키워드 인수를 커스터마이징할 수 있습니다.
return field_class, field_kwargs
class RelatedModel(models.Model):
name = models.CharField(max_length=100)
class MyModel(models.Model):
related_field = models.ForeignKey(RelatedModel, on_delete=models.CASCADE)
class MyModelSerializer(CustomModelSerializer):
class Meta:
model = MyModel
fields = ['related_field']
- build_nested_field(self, field_name, relation_info, nested_depth)
- depth 옵션이 설정되었을 때, 관계형 모델 필드에 매핑되는 시리얼라이저 필드를 생성하기 위해 호출된다.
- 기본 구현은 ModelSerializer 또는 HyperlinkedModelSerializer에 기반한 중첩된 시리얼라이저 클래스를 동적으로 생성한다.
- nested_depth는 depth 옵션의 값에서 1을 뺀 값이 된다.
- relation_info 인수는 model_field, related_model, to_many, has_through_model 속성을 포함하는 튜플이다.
from rest_framework import serializers
from django.db import models
class CustomModelSerializer(serializers.ModelSerializer):
def build_nested_field(self, field_name, relation_info, nested_depth):
# super()를 사용하여 기본적인 필드 클래스와 필드 키워드 인수를 가져옵니다.
field_class, field_kwargs = super().build_nested_field(field_name, relation_info, nested_depth)
# nested_depth의 값에 따라 중첩된 관계를 다르게 처리합니다.
if nested_depth == 0:
# nested_depth가 0이면, 중첩된 관계를 가진 필드를 문자열로 처리합니다.
field_class = serializers.StringRelatedField
elif nested_depth == 1:
# nested_depth가 1이면, 중첩된 관계를 가진 필드를 주요 키로 처리합니다.
field_class = serializers.PrimaryKeyRelatedField
return field_class, field_kwargs
class NestedRelatedModel(models.Model):
name = models.CharField(max_length=100)
class RelatedModel(models.Model):
nested_related_field = models.ForeignKey(NestedRelatedModel, on_delete=models.CASCADE)
class MyModel(models.Model):
related_field = models.ForeignKey(RelatedModel, on_delete=models.CASCADE)
class MyModelSerializer(CustomModelSerializer):
class Meta:
model = MyModel
fields = ['related_field']
depth = 2 # 이곳에서 설정한 depth 값에서 1을 뺀 값이 nested_depth가 됩니다.
- 위 코드에서 build_nested_field 메서드는 nested_depth의 값에 따라 중첩된 관계를 가진 필드를 다르게 처리한다.
- nested_depth가 0이면, 중첩된 관계를 가진 필드를 문자열로 처리하고, nested_depth가 1이면, 중첩된 관계를 가진 필드를 주요 키로 처리한다.
-이렇게 하면 MyModelSerializer를 사용하여 MyModel의 인스턴스를 직렬화할 때, related_field 필드는 nested_depth의 값에 따라 다르게 직렬화된다.
- 여기에서는 depth 옵션을 2로 설정하여 related_field의 중첩 관계를 2단계까지 직렬화한다.
- build_property_field(self, field_name, model_class)
- 모델 클래스에서 속성이나 인수 없는 메소드에 매핑되는 시리얼라이저 필드를 생성하기 위해 호출된다.
- 기본 구현은 ReadOnlyField 클래스를 반환한다.
from rest_framework import serializers
from django.db import models
class CustomModelSerializer(serializers.ModelSerializer):
def build_property_field(self, field_name, model_class):
# 기본 구현을 가져옵니다.
# 이는 ReadOnlyField 클래스를 반환합니다.
field_class, field_kwargs = super().build_property_field(field_name, model_class)
# 필드 이름이 'custom_property'인 경우, 필드 클래스를 CharField로 변경합니다.
if field_name == 'custom_property':
field_class = serializers.CharField
return field_class, field_kwargs
class MyModel(models.Model):
@property
def custom_property(self):
return 'This is a custom property'
class MyModelSerializer(CustomModelSerializer):
class Meta:
model = MyModel
fields = ['custom_property']
- build_url_field(self, field_name, model_class)
- 시리얼라이저의 자체 URL 필드에 대한 시리얼라이저 필드를 생성하기 위해 호출된다.
- 기본 구현은 HyperlinkedIdentityField 클래스를 반환한다.
- build_unknown_field(self, field_name, model_class)
- 필드 이름이 어떠한 모델 필드나 모델 속성에도 매핑되지 않았을 때 호출된다.
- 기본 구현은 오류를 발생시키지만, 서브클래스에서는 이 동작을 커스터마이징할 수 있다.
3. HyperlinkedModelSerializer
- HyperlinkedModelSerializer 클래스는 ModelSerializer 클래스와 유사하지만 관계를 표현할 때 기본 키 대신 하이퍼링크를 사용한다.
- 기본적으로 이 시리얼라이저는 기본 키 필드 대신 url 필드를 포함한다.
- url 필드는 HyperlinkedIdentityField 시리얼라이저 필드를 사용하여 표현되며, 모델의 관계는 HyperlinkedRelatedField 시리얼라이저 필드를 사용하여 표현된다.
- fields 옵션에 기본 키를 명시적으로 포함시킬 수도 있다.
class AccountSerializer(serializers.HyperlinkedModelSerializer):
class Meta:
model = Account
fields = ['url', 'id', 'account_name', 'users', 'created']
1) 절대 경로와 상대 경로 URL
- HyperlinkedModelSerializer를 인스턴스화할 때는 시리얼라이저 컨텍스트에 현재 요청을 포함해야 한다.
serializer = AccountSerializer(queryset, context={'request': request})
- 이렇게 하면 하이퍼링크에 적절한 호스트 이름을 포함할 수 있으므로, 결과적으로 완전한 URL을 사용하는 표현이 생성된다.
http://api.example.com/accounts/1/
- 상대적인 URL이 아닌 완전한 URL을 사용한다.
/accounts/1/
- 만약 상대적인 URL을 사용하려면, 시리얼라이저 컨텍스트에 {'request': None}를 명시적으로 전달해야 한다.
2) 하이퍼링크된 뷰는 어떻게 결정되는가?
- 모델 인스턴스에 대한 하이퍼링크를 생성하는 데 사용될 뷰를 결정하는 방법이 필요하다.
- 기본적으로 하이퍼링크는 '{model_name}-detail'와 같은 스타일의 뷰 이름에 해당하는 인스턴스를 pk 키워드 인자로 조회하는 것으로 예상된다.
- extra_kwargs 설정에서 view_name과 lookup_field 옵션 중 하나 또는 둘 다를 사용하여 URL 필드의 뷰 이름과 조회 필드를 재정의할 수 있다.
class AccountSerializer(serializers.HyperlinkedModelSerializer):
class Meta:
model = Account
fields = ['account_url', 'account_name', 'users', 'created']
extra_kwargs = {
'url': {'view_name': 'accounts', 'lookup_field': 'account_name'},
'users': {'lookup_field': 'username'}
}
- 또는 직접 시리얼라이저에서 필드를 설정할 수도 있다.
class AccountSerializer(serializers.HyperlinkedModelSerializer):
url = serializers.HyperlinkedIdentityField(
view_name='accounts',
lookup_field='slug'
)
users = serializers.HyperlinkedRelatedField(
view_name='user-detail',
lookup_field='username',
many=True,
read_only=True
)
class Meta:
model = Account
fields = ['url', 'account_name', 'users', 'created']
- 팁: 하이퍼링크된 표현과 URL 구성을 정확하게 매칭하는 것은 때로는 약간 까다로울 수 있다.
- HyperlinkedModelSerializer 인스턴스의 repr을 출력하는 것은 관계가 매핑되는 뷰 이름과 조회 필드를 정확하게 확인하는 유용한 방법이다.
3) URL 필드의 이름을 변경
- URL 필드의 이름은 기본적으로 'url'로 설정되어 있다.
- 전역적으로 이를 변경하려면 URL_FIELD_NAME 설정을 사용할 수 있다.
4. ListSerializer
- ListSerializer 클래스는 한 번에 여러 객체를 직렬화하고 유효성을 검사하는 동작을 제공한다.
- 일반적으로 ListSerializer를 직접 사용할 필요는 없고, 대신 직렬화기를 인스턴스화할 때 many=True를 전달하면 된다.
- 직렬화기가 인스턴스화되고 many=True가 전달되면 ListSerializer 인스턴스가 생성된다.
- 그리고 직렬화기 클래스는 부모 ListSerializer의 하위 요소가 된다.
- 또한 many=True가 전달되는 경우 ListSerializer 필드나 직렬화기에 다음과 같은 인수를 전달할 수도 있다.
- allow_empty
- 기본적으로 True로 설정되어 있지만, 유효한 입력으로 빈 리스트를 허용하지 않으려면 False로 설정할 수 있다.
- max_length
- 기본적으로 이 값은 None으로 설정되어 있지만, 이 값을 양의 정수로 설정하면 리스트가 이 정수 이상의 요소를 포함하지 않도록 유효성을 검사할 수 있다.
- min_length
- 기본적으로 이 값은 None으로 설정되어 있지만, 이 값을 양의 정수로 설정하면 리스트가 이 정수 이하의 요소를 포함하지 않도록 유효성을 검사할 수 있다.
1) ListSerializer 동작 사용자 정의하기
- ListSerializer 동작을 사용자 정의해야 하는 몇 가지 상황이 있을 수 있다.
- 예를 들어, 리스트에서 요소 간 충돌이 없는지 확인하는 것과 같은 특정한 유효성 검사를 제공하고 싶을 때, 여러 개의 객체의 생성 또는 업데이트 동작을 사용자 정의하고 싶을 때, 이러한 경우에는 serializer Meta 클래스의 list_serializer_class 옵션을 사용하여 many=True이 전달될 때 사용되는 클래스를 수정할 수 있다.
from rest_framework import serializers
class CustomListSerializer(serializers.ListSerializer):
def validate(self, data):
# 리스트의 특정한 유효성 검사 로직을 구현합니다.
# 예를 들어, 요소들 간의 충돌을 확인하는 등의 로직을 추가할 수 있습니다.
# 유효성 검사를 통과하지 못하는 경우 serializers.ValidationError를 발생시킵니다.
return data
def create(self, validated_data):
# 여러 개의 객체를 생성하기 위한 동작을 사용자 정의합니다.
# validated_data를 기반으로 새로운 객체를 생성하고 반환합니다.
# 필요에 따라 다른 로직을 추가할 수 있습니다.
pass
def update(self, instance, validated_data):
# 여러 개의 객체를 업데이트하기 위한 동작을 사용자 정의합니다.
# instance와 validated_data를 기반으로 객체를 업데이트하고 반환합니다.
# 필요에 따라 다른 로직을 추가할 수 있습니다.
pass
class CustomSerializer(serializers.Serializer):
# Meta 클래스에서 list_serializer_class 옵션을 사용하여 CustomListSerializer를 지정합니다.
class Meta:
list_serializer_class = CustomListSerializer
- 여러 개의 생성을 사용자 정의하는 방법
- 여러 개의 객체를 생성하는 기본 구현은 간단히 목록의 각 항목에 대해 .create()를 호출하는 것이다.
- 이 동작을 사용자 정의하려면 many=True가 전달될 때 사용되는 ListSerializer 클래스의create() 메서드를 사용자 정의해야 한다.
class BookListSerializer(serializers.ListSerializer):
def create(self, validated_data):
books = [Book(**item) for item in validated_data]
return Book.objects.bulk_create(books)
class BookSerializer(serializers.Serializer):
...
class Meta:
list_serializer_class = BookListSerializer
- 여러 개의 업데이트를 사용자 정의하는 방법
- 기본적으로 ListSerializer 클래스는 여러 개의 업데이트를 지원하지 않는다.
- 이는 삽입과 삭제에 대한 예상 동작이 모호하기 때문이다.
- 여러 개의 업데이트를 지원하려면 명시적으로 처리해야 한다.
- 여러 개의 업데이트 코드를 작성할 때 다음 사항을 염두해야 한다.
- 데이터 목록의 각 항목에 대해 어떤 인스턴스를 업데이트해야 하는지 어떻게 결정는가?
- 삽입은 어떻게 처리해야 하는가?
- 잘못된 것으로 간주되거나 새로운 객체를 생성하는가?
- 삭제는 어떻게 처리해야 하는가?
- 객체 삭제를 의미하거나 관계를 제거해야 하는가?
- 조용히 무시되어야 하거나 잘못된 것으로 간주되어야 하는가?
- 정렬은 어떻게 처리해야 하는가?
- 두 항목의 위치를 변경하는 것은 상태 변경을 의미하거나 무시되어야 하나요?
- 인스턴스 시리얼라이저에 명시적으로 id 필드를 추가해야 한다.
- 기본으로 암묵적으로 생성된 id 필드는 read_only로 표시되어 있다.
- 이로 인해 업데이트 시 제거된다.
- 명시적으로 선언하면 목록 시리얼라이저의 업데이트 메서드에서 사용할 수 있다.
class BookListSerializer(serializers.ListSerializer):
def update(self, instance, validated_data):
# Maps for id->instance and id->data item.
book_mapping = {book.id: book for book in instance}
data_mapping = {item['id']: item for item in validated_data}
# Perform creations and updates.
ret = []
for book_id, data in data_mapping.items():
book = book_mapping.get(book_id, None)
if book is None:
ret.append(self.child.create(data))
else:
ret.append(self.child.update(book, data))
# Perform deletions.
for book_id, book in book_mapping.items():
if book_id not in data_mapping:
book.delete()
return ret
class BookSerializer(serializers.Serializer):
# We need to identify elements in the list using their primary key,
# so use a writable field here, rather than the default which would be read-only.
id = serializers.IntegerField()
...
class Meta:
list_serializer_class = BookListSerializer
- 3.1 버전과 함께 제공되는 서드파티 패키지에는 REST framework 2에서 제공되었던 allow_add_remove 동작과 유사한 여러 개의 업데이트 작업을 자동으로 지원하는 기능이 포함될 수 있다.
- ListSerializer의 초기화를 사용자 정의하는 방법
- many=True로 직렬화기(serializer)를 인스턴스화할 때, 자식 시리얼라이저 클래스와 부모 ListSerializer 클래스의 .init() 메서드에 전달해야 하는 인수와 키워드 인수를 결정해야 한다.
- 기본 구현은 유효성 검사기(validators)와 사용자 정의 키워드 인수를 제외한 모든 인수를 두 클래스에 전달하는 것이다.
- 이는 모두 자식 시리얼라이저 클래스를 위한 것으로 가정된다.
- 가끔씩 many=True가 전달될 때 자식 및 부모 클래스가 어떻게 인스턴스화되어야 하는지 명시적으로 지정해야 할 수도 있다.
- 이를 위해 many_init 클래스 메서드를 사용할 수 있\다.
@classmethod
def many_init(cls, *args, **kwargs):
# Instantiate the child serializer.
kwargs['child'] = cls()
# Instantiate the parent list serializer.
return CustomListSerializer(*args, **kwargs)
from rest_framework import serializers
class ChildSerializer(serializers.Serializer):
def __init__(self, *args, **kwargs):
# 자식 시리얼라이저 클래스의 초기화 메서드
super().__init__(*args, **kwargs)
# 추가적인 초기화 작업 수행
class ParentListSerializer(serializers.ListSerializer):
def __init__(self, *args, **kwargs):
# 부모 ListSerializer 클래스의 초기화 메서드
super().__init__(*args, **kwargs)
# 추가적인 초기화 작업 수행
class MySerializer(serializers.Serializer):
children = ChildSerializer(many=True)
@classmethod
def many_init(cls, *args, **kwargs):
# many=True가 전달될 때 자식 및 부모 클래스의 초기화 방법을 명시적으로 지정하는 메서드
# 필요에 따라 인수와 키워드 인수를 조정할 수 있습니다.
kwargs['child'] = ChildSerializer(*args, **kwargs)
kwargs['child'].bind(field_name='', parent=self)
return ParentListSerializer(*args, **kwargs)
- 위의 코드에서 MySerializer 클래스는 ChildSerializer를 many=True로 설정하여 사용하고 있다.
- MySerializer 클래스에 many_init 클래스 메서드를 구현하여 자식 및 부모 클래스의 초기화 방법을 명시적으로 지정할 수 있다.
- 필요한 경우 child 인수와 ParentListSerializer를 인스턴스화할 때 필요한 다른 인수 및 키워드 인수를 조정할 수 있다.
- 이 코드는 many=True로 직렬화기를 인스턴스화할 때 자식 및 부모 클래스의 초기화 방법을 사용자 정의할 수 있는 방법을 보여주는 예시이다.
5. BaseSerializer
- BaseSerializer는 특정 상황에서 매우 유용하게 사용될 수 있다.
- 그러나 실제로는 대부분의 경우 Serializer 또는 ModelSerializer가 더 흔히 사용되는 편이다.
- 이는 이 두 클래스가 Django 모과의 통합이 잘 되어 있고, 필드 유효성 검증, HTML 폼 생성 등의 추가 기능을 제공하기 때문이다.
- 그럼에도 불구하고, BaseSerializer를 사용하는 경우는 다음과 같다:
1) 복잡한 데이터 구조를 다루는 경우: BaseSerializer 복잡한 데이터 구조를 처리하는 데 특히 유용하다.
- 예를 들어, 일반적인 필드 구조로는 표현하기 어려운 복잡한 JSON 데이터를 직렬화하거나 역직렬화해야 하는 경우에 이 클래스를 사용할 수 있다.
2) 특정 직렬화 스타일을 구현하려는 경우: BaseSerializer는 커스텀 직렬화 로직을 구현하는 데 유용하다.
- 이 클래스를 상속받아 .to_representation() 및 .to_internal_value() 메소드를 오버라이드으로써 고유한 직렬화 및 역직렬화 방식을 정의할 있다.
3) 대체 저장 백엔드와 통합하려는 경우: BaseSerializer는 Django 모델에 의존하지 않기 때문에, Django 모델이 아닌 다른 저장 백엔드와 통합하는 데 사용할 수 있다.
- BaseSerializer 클래스는 다양한 직렬화 및 역직렬화 스타일을 쉽게 지원할 수 있도록 사용될 수 있다.
- 이 클래스는 Serializer 클래스와 동일한 기본 API를 구현한다.
- .data - 나가는 기본 표현을 반환한다.
- .is_valid() - 들어오는 데이터를 역직렬화하고 유효성을 검사한다.
- .validated_data - 검증된 들어온 데이터를 반환한다.
- .errors - 유효성 검사 중 발생한 모든 오류를 반환한다.
- .save() - 검증된 데이터를 객체 인스턴스에 저장한다.
- 예시 :
from rest_framework import serializers
class PersonSerializer(serializers.BaseSerializer):
def to_representation(self, instance):
# Return outgoing primitive representation
return {
'name': instance.name,
'age': instance.age
}
def to_internal_value(self, data):
# Deserialize and validate incoming data
if not isinstance(data, dict):
raise serializers.ValidationError("Invalid data. Expected a dictionary.")
if 'name' not in data or 'age' not in data:
raise serializers.ValidationError("Invalid data. 'name' and 'age' are required.")
# You can add more validations here, e.g., for age to be a positive integer
return data
def create(self, validated_data):
# Persist the validated data into a Person object instance
return Person.objects.create(**validated_data)
def update(self, instance, validated_data):
# Update the Person object instance with validated data
instance.name = validated_data.get('name', instance.name)
instance.age = validated_data.get('age', instance.age)
instance.save()
return instance
# Usage
serializer = PersonSerializer(data={'name': 'John Doe', 'age': 30})
if serializer.is_valid():
person = serializer.save()
print(serializer.data) # Returns the outgoing primitive representation
print(serializer.validated_data) # Returns the validated incoming data
else:
print(serializer.errors) # Returns any errors during validation
- 시리얼라이저 클래스가 지원하길 원하는 기능에 따라 오버라이드(재정의)할 수 있는 네 가지 메소드가 있다.
- .to_representation() - 읽기 작업을 위한 직렬화를 지원하려면 이를 오버라이드한다.
- .to_internal_value() - 쓰기 작업을 위한 역직렬화를 지원하려면 이를 오버라이드한다.
- .create() 와 .update() - 인스턴스 저장을 지원하려면 이 둘 중 하나 혹은 둘 다를 오버라이드한다.
- 예시
from rest_framework import serializers
from datetime import datetime
class BookSerializer(serializers.BaseSerializer):
def to_representation(self, instance):
# Support serialization, for read operations
'''
이 메소드는 주로 '읽기' 작업에 사용됩니다.
이 메소드는 Django 모델 인스턴스를 받아서
클라이언트에게 전송될 수 있는 Python 원시 타입으로 변환합니다.
예를 들어, Book 모델 인스턴스를 받아서 딕셔너리 형태로 변환하고
이를 JSON으로 직렬화하여 클라이언트에게 보낼 수 있다.
'''
return {
'title': instance.title,
'author': instance.author,
'publication_date': instance.publication_date.strftime('%Y-%m-%d')
}
def to_internal_value(self, data):
# Support deserialization, for write operations
'''
이 메소드는 주로 '쓰기' 작업에 사용됩니다.
클라이언트로부터 받은 데이터를 Django 모델 인스턴스를 생성하거나
업데이트하는 데 필요한 Python 원시 타입으로 변환합니다.
예를 들어, 클라이언트로부터 받은 JSON 데이터를 파이썬 딕셔너리로 역직렬화하고,
이를 Book 모델 인스턴스를 생성하거나 업데이트하는 데 사용한다.
'''
if not isinstance(data, dict):
raise serializers.ValidationError("Invalid data. Expected a dictionary.")
if 'title' not in data or 'author' not in data or 'publication_date' not in data:
raise serializers.ValidationError("Invalid data. 'title', 'author', and 'publication_date' are required.")
# Convert the date string to a date object
data['publication_date'] = datetime.strptime(data['publication_date'], '%Y-%m-%d').date()
return data
def create(self, validated_data):
# Support saving instances, for create operations
return Book.objects.create(**validated_data)
def update(self, instance, validated_data):
# Support saving instances, for update operations
instance.title = validated_data.get('title', instance.title)
instance.author = validated_data.get('author', instance.author)
instance.publication_date = validated_data.get('publication_date', instance.publication_date)
instance.save()
return instance
# Usage
serializer = BookSerializer(data={'title': 'The Great Gatsby', 'author': 'F. Scott Fitzgerald', 'publication_date': '1925-04-10'})
if serializer.is_valid():
book = serializer.save()
print(serializer.data) # Returns the serialized representation
print(serializer.validated_data) # Returns the validated incoming data
else:
print(serializer.errors) # Returns validation errors
- 이 클래스는 Serializer 클래스와 동일한 인터페이스를 제공하기 때문에, 일반 Serializer 또는 ModelSerializer를 사용하는 것처럼 기존의 제네릭 클래스 기반 뷰와 함께 사용할 수 있다.
- 그러나 이를 사용할 때 주의할 점은, BaseSerializer 클래스는 브라우저블 API에서 HTML 양식을 생성하지 않는다는 것이다.
- 이는 그들이 반환하는 데이터가 각 필드를 적절한 HTML 입력으로 렌더링할 수 있을만큼 모든 필드 정보를 포함하지 않기 때문이다.
- BaseSerializer는 Serializer와 사용 방법은 동일하지만, 웹 브라우저에서 확인 가능한 API 화면에 HTML 폼을 표시하지 않는다는 차이점이 있다.
-이는 BaseSerializer가 복잡한 데이터 구조를 다루는 데 적합하게 설계되었기 때문에, 각 필드를 HTML 입력으로 표현하는 것이 적합하지 않을 수 있기 때문이다.
1) Read-only BaseSerializer classes
- BaseSerializer 클래스를 사용하여 읽기 전용 시리얼라이저를 구현하려면, .to_representation() 메소드를 오버라이드하면 된다.
- 간단한 Django 모델을 사용한 예시를 살펴보자
class HighScore(models.Model):
created = models.DateTimeField(auto_now_add=True)
player_name = models.CharField(max_length=10)
score = models.IntegerField()
- HighScore 인스턴스를 원시 데이터 타입으로 변환하는 읽기 전용 시리얼라이저를 생성하는 것은 간단하다.
class HighScoreSerializer(serializers.BaseSerializer):
def to_representation(self, instance):
return {
'score': instance.score,
'player_name': instance.player_name
}
- 이제 이 클래스를 사용하여 단일 HighScore 인스턴스를 직렬화할 수 있다.
@api_view(['GET'])
def high_score(request, pk):
instance = HighScore.objects.get(pk=pk)
serializer = HighScoreSerializer(instance)
return Response(serializer.data)
- 또는 이를 사용하여 여러 인스턴스를 직렬화할 수도 있다.
@api_view(['GET'])
def all_high_scores(request):
queryset = HighScore.objects.order_by('-score')
serializer = HighScoreSerializer(queryset, many=True)
return Response(serializer.data)
2) Read-write BaseSerializer classes
- 읽기-쓰기 시리얼라이저를 생성하려면 먼저 .to_internal_value() 메소드를 구현해야 한다.
- 이 메소드는 객체 인스턴스를 구성하는 데 사용될 검증된 값을 반환하며, 제공된 데이터가 잘못된 형식인 경우 serializers.ValidationError를 발생시킬 수 있다.
- .to_internal_value()를 구현하면 기본 검증 API가 시리얼라이저에 사용 가능해지고, .is_valid(), .validated_data 및 .errors를 사용할 수 있게 된다.
- .save()를 지원하려면 .create()와 .update() 메소드 중 하나 또는 둘 다를 구현해야 한다.
- 다음은 이전의 HighScoreSerializer를 업데이트하여 읽기 및 쓰기 작업을 모두 지원하도록 한 완전한 예시이다.
class HighScoreSerializer(serializers.BaseSerializer):
def to_internal_value(self, data):
score = data.get('score')
player_name = data.get('player_name')
# Perform the data validation.
if not score:
raise serializers.ValidationError({
'score': 'This field is required.'
})
if not player_name:
raise serializers.ValidationError({
'player_name': 'This field is required.'
})
if len(player_name) > 10:
raise serializers.ValidationError({
'player_name': 'May not be more than 10 characters.'
})
# Return the validated values. This will be available as
# the `.validated_data` property.
return {
'score': int(score),
'player_name': player_name
}
def to_representation(self, instance):
return {
'score': instance.score,
'player_name': instance.player_name
}
def create(self, validated_data):
return HighScore.objects.create(**validated_data)
3) Creating new base classes
- BaseSerializer 클래스는 특정 직렬화 스타일을 다루거나, 대체 저장 백엔드와 통합하는 새로운 일반 시리얼라이저 클래스를 구현하려는 경우에도 유용하다.
- 다음 클래스는 임의의 복잡한 객체를 원시 표현으로 강제하는 일반 시리얼라이저의 예시이다.
class ObjectSerializer(serializers.BaseSerializer):
"""
A read-only serializer that coerces arbitrary complex objects
into primitive representations.
"""
def to_representation(self, instance):
output = {}
for attribute_name in dir(instance):
attribute = getattr(instance, attribute_name)
if attribute_name.startswith('_'):
# Ignore private attributes.
pass
elif hasattr(attribute, '__call__'):
# Ignore methods and other callables.
pass
elif isinstance(attribute, (str, int, bool, float, type(None))):
# Primitive types can be passed through unmodified.
output[attribute_name] = attribute
elif isinstance(attribute, list):
# Recursively deal with items in lists.
output[attribute_name] = [
self.to_representation(item) for item in attribute
]
elif isinstance(attribute, dict):
# Recursively deal with items in dictionaries.
output[attribute_name] = {
str(key): self.to_representation(value)
for key, value in attribute.items()
}
else:
# Force anything else to its string representation.
output[attribute_name] = str(attribute)
return output
6. Advanced serializer usage
1) 직렬화와 역직렬화 동작 오버라이딩
- 시리얼라이저 클래스의 직렬화 또는 역직렬화 동작을 변경해야 하는 경우, .to_representation() 또는 .to_internal_value() 메소드를 오버라이드하여 그렇게 할 수 있다.
- 이것이 유용할 수 있는 몇 가지 이유는 다음과 같다.
- 새로운 시리얼라이저 기본 클래스에 대한 새로운 동작 추가.
- 기존 클래스에 대한 동작을 약간 수정.
- 많은 데이터를 반환하는 자주 접근되는 API 엔드포인트의 직렬화 성능 향상.
- 이러한 메소드들의 시그니처는 다음과 같다.
- to_representation(self, instance)
- 직렬화가 필요한 객체 인스턴스를 받아 원시 표현을 반환해야 한다.
- 일반적으로 이는 내장된 파이썬 데이터 타입의 구조를 반환하는 것을 의미한다.
- 처리할 수 있는 정확한 타입은 API에 설정된 렌더 클래스에 따라 달라진다.
- 표현 스타일을 수정하기 위해 오버라이드할 수 있다
def to_representation(self, instance):
"""Convert `username` to lowercase."""
ret = super().to_representation(instance)
ret['username'] = ret['username'].lower()
return ret
from rest_framework import serializers
class PersonSerializer(serializers.Serializer):
first_name = serializers.CharField(max_length=100)
last_name = serializers.CharField(max_length=100)
def to_representation(self, instance):
# 여기에 원하는 사용자 정의 표현을 구현합니다.
# 예를 들어, 이름과 성을 하나의 문자열로 합치고 싶다면 다음과 같이 할 수 있습니다.
full_name = f"{instance.first_name} {instance.last_name}"
return full_name
- to_internal_value(self, data)
- 이 메소드는 검증되지 않은 들어오는 데이터를 입력으로 받아, serializer.validated_data로 사용될 검증된 데이터를 반환해야 한다.
- 반환 값은 또한 시리얼라이저 클래스에 .save()가 호출되면 .create() 또는 .update() 메소드로 전달된다.
- 어떠한 검증이 실패하면, 이 메소드는 serializers.ValidationError(errors)를 발생시켜야 한다.
- errors 인자는 필드 이름 (또는 settings.NON_FIELD_ERRORS_KEY)을 에러 메시지의 리스트에 매핑하는 사전이어야 한다.
- 역직렬화 동작을 변경할 필요가 없고 대신 객체 수준의 검증을 제공하려는 경우, .validate() 메소드를 오버라이드하는 것이 권장된다.
- 이 메소드에 전달된 data 인자는 일반적으로 request.data의 값이므로, 제공하는 데이터 타입은 API에 설정된 파서 클래스에 따라 달라진다.
from rest_framework import serializers
class PersonSerializer(serializers.Serializer):
first_name = serializers.CharField(max_length=100)
last_name = serializers.CharField(max_length=100)
def to_internal_value(self, data):
# 데이터 검증을 위해 to_internal_value를 오버라이드합니다.
# 첫 번째로, 부모 클래스의 to_internal_value를 호출하여 기본 검증을 수행합니다.
validated_data = super().to_internal_value(data)
# 이후, 추가적인 검증을 수행할 수 있습니다.
# 예를 들어, 이름과 성이 모두 제공되었는지 확인합니다.
if 'first_name' not in validated_data or 'last_name' not in validated_data:
raise serializers.ValidationError("Both first name and last name are required.")
# 모든 검증이 통과하면, 검증된 데이터를 반환합니다.
return validated_data
2) 시리얼라이저 상속
- Django 폼과 유사하게, 상속을 통해 시리얼라이저를 확장하고 재사용할 수 있다.
- 이를 통해 여러 시리얼라이저에서 사용할 수 있는 공통적인 필드나 메소드를 부모 클래스에 선언할 수 있다.
class MyBaseSerializer(Serializer):
my_field = serializers.CharField()
def validate_my_field(self, value):
...
class MySerializer(MyBaseSerializer):
...
- Django의 Model과 ModelForm 클래스와 같이, 시리얼라이저의 내부 Meta 클래스는 부모의 내부 Meta 클래스로부터 암시적으로 상속받지 않는다.
- 만약 Meta 클래스가 부모 클래스로부터 상속을 받길 원한다면, 이를 명시적으로 해야 한다.
class AccountSerializer(MyBaseSerializer):
class Meta(MyBaseSerializer.Meta):
model = Account
- 일반적으로, 내부 Meta 클래스에서 상속을 사용하는 것보다는 모든 옵션을 명시적으로 선언하는 것을 권장한다.
- 추가로, 시리얼라이저 상속에 대해 다음과 같은 주의점들이 적용된다
- 일반적인 파이썬 이름 해석 규칙이 적용된다.
- 만약 여러 기본 클래스가 Meta 내부 클래스를 선언한 경우, 첫 번째 클래스만 사용된다.
- 이는 자식의 Meta가 존재한다면 그것을, 그렇지 않다면 첫 번째 부모의 Meta를 사용한다는 의미이다.
- 부모 클래스로부터 상속받은 필드를 서브클래스에서 이름을 None으로 설정함으로써 선언적으로 제거하는 것이 가능하다.
class MyBaseSerializer(ModelSerializer):
my_field = serializers.CharField()
class MySerializer(MyBaseSerializer):
my_field = None
- 그러나, 이 기법은 부모 클래스가 선언적으로 정의한 필드에서만 사용할 수 있다.
- 이는 ModelSerializer가 기본 필드를 생성하는 것을 방지하지 않는다.
- 기본 필드에서 선택적으로 제외하려면, 포함할 필드 지정을 참조해야 한다.
- 이 기법은 부모 클래스에서 선언적으로 정의한 필드를 선택적으로 제외하는 데 사용할 수 있다.
- 하지만 이렇게 해도 ModelSerializer가 기본 필드를 생성하는 것은 막을 수 없다.
- 기본 필드를 제외하려면, 어떤 필드를 포함할 것인지를 명시해야 한다.
- 예를 들어, 부모 클래스에서 선언적으로 정의한 필드를 제외하려면 다음과 같이 할 수 있다.
from rest_framework import serializers
class ParentSerializer(serializers.Serializer):
field1 = serializers.CharField(max_length=100)
field2 = serializers.CharField(max_length=100)
class ChildSerializer(ParentSerializer):
field1 = None
- 위의 코드에서 ChildSerializer는 ParentSerializer의 field1을 상속받지 않는다.
- 하지만 이렇게 해도 ModelSerializer가 기본 필드를 생성하는 것은 막을 수 없다.
- 예를 들어, 모델에 field3이라는 필드가 있고 이를 ModelSerializer가 자동으로 생성하도록 설정되어 있다면, field3은 여전히 ChildSerializer에 포함된다.
- 이를 제외하려면 다음과 같이 Meta 클래스에서 fields 옵션을 명시적으로 설정해야 한다.
class ChildSerializer(ParentSerializer):
class Meta:
fields = ['field2']
- 이 코드는 ChildSerializer가 field2만 포함하도록 설정한다.
- 따라서 ModelSerializer가 자동으로 생성하는 field3는 제외된다.
3) 동적으로 필드 수정하기
- 시리얼라이저가 초기화되면, 시리얼라이저에 설정된 필드의 사전은 .fields 속성을 사용하여 접근할 수 있다.
- 이 속성에 접근하고 수정하면 시리얼라이저를 동적으로 수정할 수 있다.
- 필드 인자를 직접 수정하면, 시리얼라이저를 선언하는 시점이 아니라 런타임에 시리얼라이저 필드의 인자를 변경하는 등 흥미로운 일을 할 수 있습니다.
- 예시
- 예를 들어, 시리얼라이저가 초기화될 때 어떤 필드를 사용할지 설정하고 싶다면, 다음과 같이 시리얼라이저 클래스를 생성할 수 있다.
class DynamicFieldsModelSerializer(serializers.ModelSerializer):
"""
A ModelSerializer that takes an additional `fields` argument that
controls which fields should be displayed.
"""
def __init__(self, *args, **kwargs):
# Don't pass the 'fields' arg up to the superclass
fields = kwargs.pop('fields', None)
# Instantiate the superclass normally
super().__init__(*args, **kwargs)
if fields is not None:
# Drop any fields that are not specified in the `fields` argument.
allowed = set(fields)
existing = set(self.fields)
for field_name in existing - allowed:
self.fields.pop(field_name)
- 그러면 다음과 같이 할 수 있게 된다.
>>> class UserSerializer(DynamicFieldsModelSerializer):
>>> class Meta:
>>> model = User
>>> fields = ['id', 'username', 'email']
>>>
>>> print(UserSerializer(user))
{'id': 2, 'username': 'jonwatts', 'email': 'jon@example.com'}
>>>
>>> print(UserSerializer(user, fields=('id', 'email')))
{'id': 2, 'email': 'jon@example.com'}
- 예를 들어, 사용자 프로필을 관리하는 시스템에서 관리자는 모든 필드를 볼 수 있어야 하지만, 일반 사용자는 일부 필드만 볼 수 있어야 한다고 가정한다.
- 위 코드에서 UserSerializer는 사용자의 'id', 'name', 'email', 'password' 필드를 가지고 있다. 그러나 __init__ 메소드에서, 요청을 보낸 사용자가 관리자가 아닌 경우 'email'과 'password' 필드를 제거하고 있다. 따라서 관리자는 모든 필드를 볼 수 있지만, 일반 사용자는 'id'와 'name' 필드만 볼 수 있게 된다.
from rest_framework import serializers
class UserSerializer(serializers.Serializer):
id = serializers.IntegerField()
name = serializers.CharField(max_length=100)
email = serializers.EmailField()
password = serializers.CharField(max_length=100)
def __init__(self, *args, **kwargs):
user = kwargs['context']['request'].user
super(UserSerializer, self).__init__(*args, **kwargs)
if not user.is_admin:
self.fields.pop('email')
self.fields.pop('password')
4) 기본 필드 커스터마이징하기
- REST 프레임워크 2는 개발자가 ModelSerializer 클래스가 기본 필드 세트를 자동으로 생성하는 방식을 재정의할 수 있는 API를 제공하다.
- 이 API에는 .get_field(), .get_pk_field() 및 기타 메소드가 포함되어 있었다.
- 그러나 3.0으로 시리얼라이저가 기본적으로 재설계되었기 때문에 이 API는 더 이상 존재하지 않는다.
- 여전히 생성되는 필드를 수정할 수는 있지만, 소스 코드를 참조해야 하며, 변경사항이 비공개 API에 반한다면 변경될 수 있음을 알아두어야 한다.
7. Third party packages
- jango REST marshmallow
- django-rest-marshmallow 패키지는 python marshmallow 라이브러리를 사용한 시리얼라이저에 대한 대체 구현을 제공합니다. 이는 REST 프레임워크 시리얼라이저와 동일한 API를 노출하며, 일부 사용 사례에서 드롭인 교체로 사용할 수 있습니다.
- Serpy
- serpy 패키지는 속도를 위해 만들어진 시리얼라이저에 대한 대체 구현입니다. Serpy는 복잡한 데이터 타입을 간단한 기본 타입으로 직렬화합니다. 이 기본 타입은 쉽게 JSON이나 다른 필요한 형식으로 변환할 수 있습니다.
- MongoengineModelSerializer
- django-rest-framework-mongoengine 패키지는 Django REST 프레임워크의 저장 계층으로 MongoDB를 사용하는 것을 지원하는 MongoEngineModelSerializer 시리얼라이저 클래스를 제공합니다.
- GeoFeatureModelSerializer
- django-rest-framework-gis 패키지는 읽기와 쓰기 작업 모두에 대해 GeoJSON을 지원하는 GeoFeatureModelSerializer 시리얼라이저 클래스를 제공합니다.
- HStoreSerializer
- django-rest-framework-hstore 패키지는 django-hstore DictionaryField 모델 필드와 그 스키마 모드 기능을 지원하는 HStoreSerializer를 제공합니다.
- Dynamic REST
- dynamic-rest 패키지는 ModelSerializer와 ModelViewSet 인터페이스를 확장하며, 필터링, 정렬, 그리고 시리얼라이저에 의해 정의된 모든 필드와 관계를 포함/제외하는 API 쿼리 매개 변수를 추가합니다.
- Dynamic Fields Mixin
- drf-dynamic-fields 패키지는 URL 매개 변수로 지정된 부분 집합으로 시리얼라이저 당 필드를 동적으로 제한하는 믹스인을 제공합니다.
- DRF FlexFields
- drf-flex-fields 패키지는 ModelSerializer와 ModelViewSet를 확장하여 필드를 동적으로 설정하고 원시 필드를 중첩 모델로 확장하는 일반적으로 사용되는 기능을 제공합니다. 이는 URL 매개 변수 및 시리얼라이저 클래스 정의 모두에서 가능합니다.
- Serializer Extensions
- django-rest-framework-serializer-extensions 패키지는 필드가 뷰/요청 기반으로 정의되도록 허용함으로써 시리얼라이저를 DRY(Do not Repeat Yourself)하게 만드는 도구 모음을 제공합니다. 필드는 화이트리스트에 올릴 수 있으며, 블랙리스트에 올릴 수 있으며, 자식 시리얼라이저는 선택적으로 확장될 수 있습니다.
- HTML JSON Forms
- html-json-forms 패키지는 (비활성) HTML JSON Form 사양에 따른 <form> 제출을 처리하기 위한 알고리즘과 시리얼라이저를 제공합니다. 시리얼라이저는 HTML 내에서 임의로 중첩된 JSON 구조의 처리를 용이하게 합니다. 예를 들어, <input name="items[0][id]" value="5">는 {"items": [{"id": "5"}]}로 해석됩니다.
- DRF-Base64
- DRF-Base64는 base64로 인코딩된 파일의 업로드를 처리하는 필드 및 모델 시리얼라이저의 세트를 제공합니다.
- QueryFields
- djangorestframework-queryfields는 API 클라이언트가 응답에 포함될 필드를 지정할 수 있게 하는 포함/제외 쿼리 매개 변수를 허용합니다.
- DRF Writable Nested
- drf-writable-nested 패키지는 중첩된 관련 데이터와 함께 모델을 생성/업데이트 할 수 있게 하는 쓰기 가능한 중첩 모델 시리얼라이저를 제공합니다.
- DRF Encrypt Content
- drf-encrypt-content 패키지는 ModelSerializer를 통해 직렬화된 데이터를 암호화하는 데 도움이 되는 패키지입니다. 또한 데이터를 암호화하는 데 도움이 되는 몇 가지 도우미 함수를 포함하고 있습니다.
- 공식 사이트 문서 :
https://www.django-rest-framework.org/api-guide/serializers/#serializers
https://www.django-rest-framework.org/api-guide/fields/
https://www.django-rest-framework.org/api-guide/relations/