시리얼라이저의 유용성을 확장하는 것은 우리가 다루고 싶어하는 주제입니다. 그러나 이것은 간단한 문제가 아니며, 심각한 설계 작업이 필요할 것입니다.
— Russell Keith-Magee, Django 사용자 그룹
시리얼라이저는 쿼리셋과 모델 인스턴스와 같은 복잡한 데이터를 원시 Python 데이터 유형으로 변환하여 이를 간단하게 JSON
, XML
또는 다른 콘텐츠 유형으로 렌더링할 수 있도록 해줍니다. 시리얼라이저는 역직렬화도 제공하여 구문 분석된 데이터를 먼저 유효성 검사한 후에 복잡한 유형으로 다시 변환할 수 있게 해줍니다.
REST 프레임워크의 시리얼라이저는 Django의 Form
및 ModelForm
클래스와 매우 유사하게 작동합니다. 우리는 출력 응답을 효과적으로 제어하는 강력하고 범용적인 방법을 제공하는 Serializer
클래스와 모델 인스턴스와 쿼리셋을 다루는 시리얼라이저를 간편하게 생성하는 유용한 바로 가기인 ModelSerializer
클래스를 제공합니다.
예제를 위해 사용할 수 있는 간단한 객체를 만들어 봅시다:
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
객체에 해당하는 데이터를 직렬화하고 역직렬화하는 데 사용할 수 있는 시리얼라이저를 선언해보겠습니다.
시리얼라이저를 선언하는 것은 폼을 선언하는 것과 매우 유사합니다:
from rest_framework import serializers
class CommentSerializer(serializers.Serializer):
email = serializers.EmailField()
content = serializers.CharField(max_length=200)
created = serializers.DateTimeField()
이제 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"}'
역직렬화도 비슷합니다. 먼저 스트림을 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)}
유효성 검사한 데이터를 기반으로 완전한 객체 인스턴스를 반환할 수 있도록 하려면 .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()
를 호출하여 유효성 검사한 데이터를 기반으로 객체 인스턴스를 반환할 수 있습니다.
comment = serializer.save()
.save()
를 호출하면 객체 인스턴스를 생성하거나 기존 인스턴스를 업데이트합니다. 이는 시리얼라이저 클래스를 인스턴스화할 때 기존 인스턴스가 전달되었는지에 따라 달라집니다.
# .save()는 새로운 인스턴스를 생성합니다.
serializer = CommentSerializer(data=data)
# .save()는 기존 `comment` 인스턴스를 업데이트합니다.
serializer = CommentSerializer(comment, data=data)
.create()
및 .update()
메서드는 모두 선택 사항입니다. 시리얼라이저 클래스의 사용 사례에 따라 아무 것도, 하나만 또는 둘 다 구현할 수 있습니다.
가끔 보기 코드에서 인스턴스를 저장하는 시점에 추가 데이터를 주입할 수 있도록 하려는 경우가 있습니다. 이 추가 데이터에는 현재 사용자, 현재 시간 또는 요청 데이터의 일부가 아닌 다른 어떤 것이든 포함될 수 있습니다.
.save()
를 호출할 때 추가 키워드 인수를 포함하여 이를 수행할 수 있습니다. 예를 들어:
serializer.save(owner=request.user)
추가 키워드 인수는 .create()
또는 .update()
가 호출될 때 validated_data
인수에 포함됩니다.
일부 경우에는 .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
속성에 접근해야 합니다.
데이터를 역직렬화할 때는 언제나 유효성 검사를 시도하기 전에 is_valid()
를 호출해야 합니다. 유효성 검사 중에 오류가 발생하면 .errors
속성에는 결과 오류 메시지를 나타내는 사전이 포함됩니다. 예를 들어:
serializer = CommentSerializer(data={'email': 'foobar', 'content': 'baz'})
serializer.is_valid()
# False
serializer.errors
# {'email': ['유효한 이메일 주소를 입력하세요.'], 'created': ['이 필드는 필수입니다.']}
사전의 각 키는 필드 이름이고 값은 해당 필드에 해당하는 오류 메시지의 문자열 목록입니다. non_field_errors
키도 존재할 수 있으며 일반적인 유효성 오류를 나열합니다. non_field_errors
키의 이름은 NON_FIELD_ERRORS_KEY
REST 프레임워크 설정을 사용하여 사용자 정의할 수 있습니다.
항목 목록을 역직렬화하는 경우 오류가 발생하면 각 역직렬화된 항목을 나타내는 사전의 목록으로 반환됩니다.
.is_valid()
메서드는 유효성 검사 오류가 있는 경우 serializers.ValidationError
예외를 발생시킬 수 있는 선택적인 raise_exception
플래그를 사용할 수 있습니다.
이러한 예외는 REST 프레임워크가 제공하는 기본 예외 처리기에서 자동으로 처리되며 기본적으로 HTTP 400 Bad Request
응답을 반환합니다.
# 데이터가 유효하지 않은 경우 400 응답 반환.
serializer.is_valid(raise_exception=True)
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):
"""
블로그 글이 Django와 관련되어 있는지 확인합니다.
"""
if 'django' not in value.lower():
raise serializers.ValidationError("블로그 글이 Django와 관련되어 있지 않습니다.")
return value
참고: <field_name>
이 시리얼라이저에서 `required
=False` 매개변수로 선언된 경우 이 유효성 검사 단계는 해당 필드가 포함되지 않는 경우에는 수행되지 않습니다.
여러 필드에 액세스할 수 있는 다른 유효성 검사가 필요한 경우 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):
"""
시작 시간이 종료 시간보다 앞인지 확인합니다.
"""
if data['start'] > data['finish']:
raise serializers.ValidationError("종료 시간은 시작 시간 이후여야 합니다.")
return data
시리얼라이저의 개별 필드는 해당 필드 인스턴스에 선언하여 유효성 검사기를 포함할 수 있습니다. 예를 들어:
def multiple_of_ten(value):
if value % 10 != 0:
raise serializers.ValidationError('10의 배수가 아닙니다')
class GameRecord(serializers.Serializer):
score = IntegerField(validators=[multiple_of_ten])
...
시리얼라이저 클래스는 필드 데이터의 전체 집합에 적용되는 재사용 가능한 유효성 검사기를 포함할 수도 있습니다. 이러한 유효성 검사기는 내부 Meta
클래스에 선언하여 포함됩니다. 예를 들어:
class EventSerializer(serializers.Serializer):
name = serializers.CharField()
room_number = serializers.IntegerField(choices=[101, 102, 103, 201])
class Meta:
# 각 방은 하루에 한 번의 이벤트만 가집니다.
validators = [
UniqueTogetherValidator(
queryset=Event.objects.all(),
fields=['room_number', 'date']
)
]
자세한 정보는 유효성 검사기 문서를 참조하세요.
시리얼라이저 인스턴스에 초기 객체나 쿼리셋을 전달할 때 해당 객체는 .instance
으로 사용할 수 있습니다. 초기 객체가 전달되지 않으면 .instance
속성은 None
이 됩니다.
시리얼라이저 인스턴스에 데이터를 전달할 때 수정되지 않은 데이터는 .initial_data
로 사용할 수 있습니다. data
키워드 인수가 전달되지 않으면 .initial_data
속성은 존재하지 않습니다.
기본적으로 필수 필드에 대해 값이 전달되지 않으면 시리얼라이저가 유효성 검사 오류를 발생시킵니다. 부분 업데이트를 허용하려면 partial
인수를 사용할 수 있습니다.
# 'content' 필드를 이용해 'comment'를 부분 데이터로 업데이트합니다.
serializer = CommentSerializer(comment, data={'content': 'foo bar'}, partial=True)
이전 예제는 단순한 데이터 유형만 갖는 객체를 다루는 데 적합하지만, 때로는 더 복잡한 객체를 나타내야 할 때도 있습니다. 객체의 일부 속성이 문자열, 날짜 또는 정수와 같은 단순한 데이터 유형이 아닐 수 있는 복잡한 객체를 표현해야 할 때도 있습니다.
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
플래그를 전달해야 합니다.
class CommentSerializer(serializers.Serializer):
user = UserSerializer(required=False) # 익명 사용자일 수 있습니다.
content = serializers.CharField(max_length=200)
created = serializers.DateTimeField()
또한 중첩된 표현이 항목 목록이 되어야 하는 경우 중첩된 시리얼라이저에 many=True
플래그를 전달해야 합니다.
class CommentSerializer(serializers.Serializer):
user = UserSerializer(required=False)
edits = EditItemSerializer(many=True) # 'edit' 항목의 중첩된 목록.
content = serializers.CharField(max_length=200)
created = serializers.DateTimeField()
중첩된 표현을 처리하는 동안 데이터를 역직렬화할 수 있는 경우, 중첩된 객체와 관련된 모든 오류는 중첩된 객체의 필드 이름 아래에 중첩됩니다.
serializer = CommentSerializer(data={'user': {'email': 'foobar', 'username': 'doe'}, 'content': 'baz'})
serializer.is_valid()
# False
serializer.errors
# {'user': {'email': ['유효한 이메일 주소를 입력하세요.']}, 'created': ['이 필드는 필수입니다.']}
비슷하게, .validated_data
속성은 중첩된 데이터 구조를 포함할 것입니다.
.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
.update()
메서드 작성업데이트를 위해 관계를 어떻게 처리할지 신중하게 생각해야 합니다. 예를 들어 관계의 데이터가 None
인 경우나 제공되지 않은 경우, 다음 중 어느 것을 취해야 할지 고려해야 합니다.
NULL
로 설정합니다.다음은 이전 UserSerializer
클래스에서 .update()
메서드의 예제입니다.
def update(self, instance, validated_data):
profile_data = validated_data.pop('profile')
# 애플리케이션이 이 필드가 항상 설정되어 있도록 올바르게 적용하지 않는 경우,
# 아래의 코드는 `DoesNotExist`를 발생시킬 수 있습니다.
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 framework 3에서는 이러한 메서드를 항상 명시적으로 작성하도록 요구합니다. 기본 ModelSerializer
의 .create()
와 .update()
메서드는 자동으로 중첩된 표현을 지원하지 않습니다.
하지만 자동으로 중첩된 표현을 지원하는 DRF Writable Nested과 같은 타사 패키지도 있습니다.
시리얼라이저에서 여러 관련 인스턴스를 저장하는 대신 올바른 인스턴스를 생성하는 사용자 정의 모델 매니저 클래스를 작성할 수 있습니다.
예를 들어, 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
이 매니저 클래스는 이제 사용자 인스턴스와 프로필 인스턴스가 항상 동시에 생성됨을 더 잘 캡슐화합니다. 이제 시리얼라이저 클래스의 .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']
)
이 접근 방식에 대한 자세한 내용은 모델 매니저 문서와 모델 및 매니저 클래스 사용에 관한 블로그 포스트를 참조하십시오.
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'}
# ]
다중 객체 역직렬화의 기본 동작은 여러 객체 생성을 지원하지만 여러 객체 업데이트를 지원하지는 않습니다. 이러한 경우를 지원하거나 사용자 정의하려면 아래의 ListSerializer 문서를 참조하십시오.
시리얼라이저에 객체를 직렬화하는 데 필요한 외부 컨텍스트를 추가로 제공해야 하는 경우가 있습니다. 일반적인 경우는 하이퍼링크 관계를 포함하는 시리얼라이저를 사용하는 경우입니다. 이 경우 시리얼라이저가 완전한 URL을 올바르게 생성할 수 있도록 현재 요청에 대한 액세스 권한이 필요합니다.
시리얼라이저를 인스턴스화할 때 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'}
컨텍스트 사전은 커스텀 .to_representation()
메서드와 같은 모든 시리얼라이저 필드 로직에서 self.context
속성을 통해 사용
할 수 있습니다.
자주 사용되는 시리얼라이저 클래스는 Django 모델 정의와 밀접하게 매핑되는 시리얼라이저 클래스를 원할 것입니다.
ModelSerializer
클래스는 다음과 같은 특징을 가지며 일반적인 Serializer
클래스와 동일합니다.
.create()
및 .update()
의 간단한 기본 구현을 포함합니다.ModelSerializer
를 선언하는 것은 다음과 같습니다.
class AccountSerializer(serializers.ModelSerializer):
class Meta:
model = Account
fields = ['id', 'account_name', 'users', 'created']
기본적으로 클래스의 모든 모델 필드는 해당하는 시리얼라이저 필드에 매핑됩니다.
모델에서 외래 키와 같은 관계가 있다면 이는 PrimaryKeyRelatedField
로 매핑됩니다. 역방향 관계는 기본적으로 포함되지 않지만 serializer relations 문서에서 명시적으로 포함되도록 지정된 경우에만 포함됩니다.
시리얼라이저 클래스는 상태를 완전히 검사할 수 있게 해주는 유용한 상세 표현 문자열을 생성합니다. 특히 ModelSerializers
와 함께 작업할 때 자동으로 생성되는 필드와 유효성 검사자 세트를 확인하려면 유용합니다.
이를 위해 Django 쉘을 열고 python manage.py shell
을 실행한 다음 시리얼라이저 클래스를 가져오고 인스턴스를 만들고 객체 표현을 출력합니다.
>>> 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())
모델 시리얼라이저에서 기본 필드의 하위 집합만 사용하려는 경우 fields
또는 exclude
옵션을 사용할 수 있습니다. 이는 ModelForm
과 동일하게 수행됩니다. 모델이 변경되었을 때 데이터를 무심코 노출시킬 가능성을 줄이기 위해 명시적으로 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__'
exclude
속성을 사용하여 시리얼라이저에서 제외될 필드 목록을 지정할 수도 있습니다.
예를 들어:
class AccountSerializer(serializers.ModelSerializer):
class Meta:
model = Account
exclude = ['users']
위의 예제에서 Account
모델에 account_name
, users
, created
라는 3개의 필드가 있다면 account_name
과 created
필드만 직렬화될 것입니다.
fields
및 exclude
속성의 이름은 일반적으로 모델 클래스의 모델 필드와 매핑됩니다.
또는 fields
옵션의 이름은 모델 클래스에서 존재하는 인수를 사용하지 않는 속성 또는 메서드에 매핑될 수 있습니다.
버전 3.3.0부터 fields
또는 exclude
속성 중 하나를 제공하는 것이 필수입니다.
기본 ModelSerializer
는 관계에 대해 기본 키를 사용하지만, depth
옵션을 사용하여 중첩된 표현을 쉽게 생성할 수도 있습니다:
class AccountSerializer(serializers.ModelSerializer):
class Meta:
model = Account
fields = ['id', 'account_name', 'users', 'created']
depth = 1
depth
옵션은 관계를 얼마나 깊게 탐색할지를 나타내는 정수 값을 설정해야 합니다. 설정된 값에 따라 관계의 깊이를 탐색한 다음 평면적인 표현으로 전환합니다.
직렬화 방식을 사용자 정의하려면 직접 필드를 정의해야 합니다.
ModelSerializer
에 추가적인 필드를 추가하거나 기본 필드를 재정의하려면 클래스에 필드를 선언하면 됩니다. 마치 Serializer
클래스와 같이 클래스 내에 필드를 선언합니다.
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=True
속성을 사용하는 대신 read_only_fields
이라는 메타 옵션을 사용할 수 있습니다.
이 옵션은 필드 이름의 리스트나 튜플이어야 하며 다음과 같이 선언됩니다:
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
옵션에 추가할 필요가 없습니다.
참고: 모델 수준에서 unique_together
제약 조건의 일부인 읽기 전용 필드의 특수한 경우가 있습니다. 이 경우 시리얼라이저 클래스에서 제약 조건을 유효성 검사하기 위해 필드가 필요하지만 사용자에 의해 편집 가능해서는 안됩니다.
이러한 경우에는 시리얼라이저에 필드를 명시적으로 지정하고 read_only=True
및 default=...
키워드 인수를 함께 제공하는 것이 올바른 방법입니다.
이와 관련한 한 가지 예는 현재 인증된 User
와 다른 식별자와 함께 unique_together
인 읽기 전용 관계입니다. 이 경우 사용자 필드는 다음과 같이 선언할 수 있습니다:
user = serializers.PrimaryKeyRelatedField(read_only=True, default=serializers.CurrentUserDefault())
[Validators 문서](https://www.django-rest-framework.org/api-guide/validators/)
를 검토하여 UniqueTogetherValidator
와 CurrentUserDefault
클래스에 대한 자세한 내용을 확인하십시오.
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
옵션이 무시될 것이라는 것입니다.
모델 인스턴스를 직렬화할 때 관계를 나타내는 다양한 방법이 있습니다. ModelSerializer
의 기본 표현 방식은 관련 인스턴스의 기본 키를 사용하는 것입니다.
대체 표현 방식으로는 하이퍼링크를 사용한 직렬화, 완전한 중첩된 표현, 또는 사용자 정의 표현을 사용하는 등이 있습니다.
자세한 내용은 serializer relations 문서를 참조하십시오.
ModelSerializer
클래스는 직렬라이저 필드가 직렬라이저 인스턴스화 시 자동으로 결정되는 방식을 변경하기 위해 재정의
할 수 있는 API를 노출합니다.
일반적으로 ModelSerializer
가 기본적으로 필요한 필드를 생성하지 않으면 필요한 필드를 직접 추가하거나, 일반 Serializer
클래스를 사용하면 됩니다. 그러나 일부 경우에는 모든 모델에 대해 직렬라이저 필드가 어떻게 생성되는지를 정의하는 새로운 기본 클래스를 생성하려는 경우가 있을 수 있습니다.
.serializer_field_mapping
Django 모델 필드를 REST 프레임워크 직렬라이저 필드로 매핑하는 맵입니다. 이 매핑을 재정의하여 각 모델 필드에 대해 사용할 기본 직렬라이저 필드를 변경할 수 있습니다.
.serializer_related_field
기본적으로 관계형 필드에 대해 사용되는 직렬라이저 필드 클래스입니다.
ModelSerializer
의 경우 기본값은 serializers.PrimaryKeyRelatedField
입니다.
HyperlinkedModelSerializer
의 경우 기본값은 serializers.HyperlinkedRelatedField
입니다.
.serializer_url_field
직렬라이저의 url
필드에 사용될 직렬라이저 필드 클래스입니다.
기본값은 serializers.HyperlinkedIdentityField
입니다.
.serializer_choice_field
직렬라이저의 선택 필드에 사용될 직렬라이저 필드 클래스입니다.
기본값은 serializers.ChoiceField
입니다.
각 필드의 클래스와 키워드 인수를 결정하기 위해 호출되는 다음 메서드는 각각 두 개의 튜플인 (field_class, field_kwargs)
를 반환해야 합니다.
.build_standard_field(self, field_name, model_field)
표준 모델 필드에 매핑되는 직렬라이저 필드를 생성하기 위해 호출됩니다.
기본 구현은 serializer_field_mapping
속성을 기반으로 직렬라이저 클래스를 반환합니다.
.build_relational_field(self, field_name, relation_info)
관계 모델 필드에 매핑되는 직렬라이저 필드를 생성하기 위해 호출됩니다.
기본 구현은 serializer_related_field
속성을 기반으로 직렬라이저 클래스를 반환합니다.
relation_info
인수는 model_field
, related_model
, to_many
, has_through_model
속성을 포함하는 네임드 튜플입니다.
.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
속성을 포함하는 네임드 튜플입니다.
.build_property_field(self, field_name, model_class)
모델 클래스의 속성 또는 인자 없는 메서드에 매핑되는 직렬라이저 필드를 생성하기 위해 호출됩니다.
기본 구현은 ReadOnlyField
클래스를 반환합니다.
.build_url_field(self, field_name, model_class)
직렬라이저의 자체 url
필드에 매핑되는 직렬라이저 필드를 생성하기 위해 호출됩니다. 기본 구현은 HyperlinkedIdentityField
클래스를 반환합니다.
.build_unknown_field(self, field_name, model_class)
필드 이름이 어떤 모델 필드나 모델 속성과도 일치하지 않는 경우에 호출됩니다. 기본 구현은 오류를 발생시키지만 하위 클래스는 이 동작을 사용자 정의할 수 있습니다.
HyperlinkedModelSerializer
클래스는 ModelSerializer
클래스와 유사하지만 관계를 나타내는 데 기본 키 대신 하이퍼링크를 사용합니다.
기본적으로 직렬라이저는 기본 키 필드 대신 url
필드를 포함합니다.
url
필드는 HyperlinkedIdentityField
직렬라이저 필드를 사용하여 나타내며, 모델의 관계는 HyperlinkedRelatedField
직렬라이저 필드를 사용하여 나타냅니다.
fields
옵션에 기본 키를 명시적으로 포함하려면 다음과
같이 수행할 수 있습니다:
class AccountSerializer(serializers.HyperlinkedModelSerializer):
class Meta:
model = Account
fields = ['url', 'id', 'account_name', 'users', 'created']
HyperlinkedModelSerializer
를 인스턴스화할 때 직렬라이저 컨텍스트에 현재 request
를 포함해야 합니다. 예를 들어:
serializer = AccountSerializer(queryset, context={'request': request})
이렇게 하면 하이퍼링크가 적절한 호스트 이름을 포함하여 나타낼 수 있으며, 결과적인 표현이 다음과 같이 완전한 경로 URL을 사용합니다:
http://api.example.com/accounts/1/
상대 URL을 사용하려면 직렬라이저 컨텍스트에 {'request': None}
을 명시적으로 전달해야 합니다.
모델 인스턴스에 대해 하이퍼링크된 뷰가 어떤 뷰를 사용해야 하는지를 결정해야 합니다.
기본적으로 하이퍼링크는 '{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
을 인쇄하면 관계가 기대되는 뷰 이름과 조회 필드가 어떻게 매핑되는지를 확인하는 유용한 방법입니다.
URL 필드의 이름은 기본적으로 'url'입니다. 이 이름을 전역적으로 변경하려면 URL_FIELD_NAME
설정을 사용할 수 있습니다.
ListSerializer
클래스는 여러 객체를 한꺼번에 직렬화하고 유효성을 검사하는 동작을 제공합니다. 보통 직접적으로 ListSerializer
를 사용하지는 않으며, 대신 직렬라이저를 인스턴스화할 때 many=True
를 전달하면 됩니다.
직렬라이저를 인스턴스화하고 many=True
를 전달하면 ListSerializer
인스턴스가 생성됩니다. 그런 다음 직렬라이저 클래스는 부모 ListSerializer
의 하위 항목이 됩니다.
ListSerializer
필드 또는 many=True
가 전달되는 직렬라이저에도 다음 인수를 전달할 수 있습니다:
allow_empty
기본적으로 이 값은 True
입니다. 그러나 빈 리스트를 유효한 입력으로 허용하지 않으려면 False
로 설정할 수 있습니다.
max_length
기본적으로 이 값은 None
입니다. 그러나 양의 정수로 설정하면 리스트에 포함된 요소 수가 이 숫자 이상이 되지 않도록 유효성을 검사합니다.
min_length
기본적으로 이 값은 None
입니다. 그러나 양의 정수로 설정하면 리스트에 포함된 요소 수가 이 숫자 이하가 되지 않도록 유효성을 검사합니다.
ListSerializer
동작ListSerializer
동작을 커스터마이즈하려는 경우 몇 가지 사용 사례가 있을 수 있습니다. 예를 들어:
이러한 경우 many=True
가 전달될 때 사용되는 클래스를 수정하려면 직렬라이저 Meta
클래스의 list_serializer_class
옵션을 사용하면 됩니다.
예를 들어:
class CustomListSerializer(serializers.ListSerializer):
...
class CustomSerializer(serializers.Serializer):
...
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
로 표시됩니다. 이로 인해 업데이트시 제거됩니다. 명시적으로 선언하면 목록 직렬라이저의 update
메서드에서 사용할 수 있습니다.
다음은 여러 개의 업데이트를 어떻게 구현할 수 있는지 예시입니다:
class BookListSerializer(serializers.ListSerializer):
def update(self, instance, validated_data):
# id -> instance 및 id -> 데이터 항목을 위한 맵.
book_mapping = {book.id: book for book in instance}
data_mapping = {item['id']: item for item in validated_data}
# 생성 및 업데이트 실행.
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))
# 삭제 실행.
for book_id, book in book_mapping.items():
if book_id not in data_mapping:
book.delete()
return ret
class BookSerializer(serializers.Serializer):
# 리스트에서 요소를 식별하기 위해 `id` 필드를 추가해야 합니다.
# 이 필드는 기본적으로 읽기 전용이며, 업데이트 시에 제거됩니다.
id = serializers.IntegerField()
...
class Meta:
list_serializer_class = BookListSerializer
세 번째 자세한 예시인 Customizing ListSerializer initialization, Read-only BaseSerializer classes, Read-write BaseSerializer classes, [Creating new base
classes](https://www.django-rest-framework.org/api-guide/serializers/#creating-new-base-classes)에 대한 내용은 상황에 따라 필요할 수 있습니다.
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
클래스가 사용됩니다.
자식 클래스에서 부모 클래스에서 상속한 Field
를 선언적으로 제거하는 것도 가능합니다. 이를 위해 서브클래스에서 이름을 None
으로 설정하면 됩니다.
class MyBaseSerializer(ModelSerializer):
my_field = serializers.CharField()
class MySerializer(MyBaseSerializer):
my_field = None
그러나 이 기법은 부모 클래스에서 선언적으로 정의한 필드에서만 사용할 수 있습니다. ModelSerializer
가 기본 필드를 생성하는 것은 방지하지 않습니다. 기본 필드에서 제외하려면 포함할 필드 지정을 참조하세요.
한 번 직렬라이저가 초기화되면 직렬라이저에 설정된 필드 사전은 .fields
속성을 사용하여 액세스할 수 있습니다. 이 속성에 액세스하고 수정함으로써 직렬라이저를 동적으로 수정할 수 있습니다.
직접 fields
인수를 수정하면 선언할 때가 아니라 런타임에 직렬라이저 필드의 인수를 변경하는 것과 같이 흥미로운 작업을 수행할 수 있습니다.
예를 들어, 직렬라이저를 초기화하는 시점에 어떤 필드를 사용해야 할지 설정할 수 있도록 하려면 다음과 같이 직렬라이저 클래스를 생성할 수 있습니다:
class DynamicFieldsModelSerializer(serializers.ModelSerializer):
"""
특정 필드가 표시되어야 할지 제어하는 추가적인 `fields` 인수를 허용하는 ModelSerializer입니다.
"""
def __init__(self, *args, **kwargs):
# 상위 클래스로 'fields' 인수를 전달하지 않습니다.
fields = kwargs.pop('fields', None)
# 상위 클래스를 정상적으로 인스턴스화합니다.
super().__init__(*args, **kwargs)
if fields is not None:
# `fields` 인수에 명시되지 않은 필드는 제거합니다.
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'}
REST framework 2에서는 개발자가 ModelSerializer
클래스가 기본 필드 세트를 자동으로 생성하는 방식을 재정의할 수 있도록 API를 제공했습니다.
이 API에는 .get_field()
, .get_pk_field()
및 기타 메서드가 포함되어 있었습니다.
3.0으로 직렬라이저가 기본적으로 재설계되면서 이 API는 더 이상 존재하지 않습니다. 여전히 생성되는 필드를 수정할 수 있지만 소스 코드를 참조해야 하며, 수정하는 변경 사항이 API의 비공개 부분에 대한 것인 경우 변경될 수 있음을 알아두어야 합니다.
다음 제3자 패키지도 사용 가능합니다.
django-rest-marshmallow 패키지는 파이썬 marshmallow 라이브러리를 사용한 대안적인 직렬라이저 구현을 제공합니다. 이 패키지는 REST framework 직렬라이저와 동일한 API를 노출하며, 특정 사용 사례에서는 이를 대체로 사용할 수 있습니다.
serpy 패키지는 속도를 위해 구축된 대안적인 직렬라이저 구현입니다. Serpy는 복잡한 데이터 유형을 간단한 네이티브 유형으로 직렬화합니다. 이 네이티브 유형은 JSON이나 다른 필요한 형식으로 쉽게 변환될 수 있습니다.
django-rest-framework-mongoengine 패키지는 MongoEngineModelSerializer
직렬라이저 클래스를 제공하며, Django REST framework에서 MongoDB를 저장소 계층으로 사용하는 데 사용할 수 있습니다.
django-rest-framework-gis 패키지는 GeoFeatureModelSerializer
직렬라이저 클래스를 제공하며, GeoJSON을 읽고 쓰는 데 모두 사용할 수 있습니다.
django-rest-framework-hstore 패키지는 HStoreSerializer
를 제공하여 django-hstore의 DictionaryField
모델 필드와 해당 schema-mode
기능을 지원합니다.
dynamic-rest 패키지는 ModelSerializer 및 ModelViewSet 인터페이스를 확장하여 필터링, 정렬 및 직렬라이저로 정의된 모든 필드와 관계에 대한 포함/제외를 위한 API 쿼리 매개변수를 추가합니다.
drf-dynamic-fields 패키지는 URL 매개변수로 지정된 하위 집합에 해당하는 필드만 동적으로 제한하기 위한 믹스인을 제공합니다.
drf-flex-fields 패키지는 ModelSerializer 및 ModelViewSet을 확장하여 URL 매개변수 및 직렬라이저 클래스 정의에서 필드를 동적으로 설정하고 원시 필드를 중첩 모델로 확장하는 일반적으로 사용되는 기능을 제공합니다.
django-rest-framework-serializer-extensions 패키지는 직렬라이저를 DRY하게 유지하기 위한 도구 모음을 제공합니다. 필드를 뷰/요청 기반으로 정의할 수 있도록 하여 필드를 화이트리스트 또는 블랙리스트로 설정하고 하위 직렬라이저를 선택적으로 확장할 수 있습니다.
html-json-forms 패키지는 (비활성화된) HTML JSON Form 사양에 따라 `<
form>제출을 처리하기 위한 알고리즘과 직렬라이저를 제공합니다. 이 직렬라이저는 HTML 내에서 임의로 중첩된 JSON 구조를 처리하는 데 도움이 됩니다. 예를 들어,
는
{"items": [{"id": "5"}]}`로 해석됩니다.
DRF-Base64 패키지는 base64로 인코딩된 파일 업로드를 처리하는 필드 및 모델 직렬라이저 세트를 제공합니다.
djangorestframework-queryfields 패키지는 API 클라이언트가 응답에서 전송할 필드를 포함/제외 쿼리 매개변수를 통해 지정할 수 있게 합니다.
drf-writable-nested 패키지는 중첩된 관련 데이터로 모델을 생성/업데이트할 수 있게 해주는 쓰기 가능한 중첩 모델 직렬라이저를 제공합니다.
drf-encrypt-content 패키지는 ModelSerializer를 통해 직렬화된 데이터를 암호화하는 데 도움이 되며, 몇 가지 보조 함수도 포함되어 있습니다. 데이터를 암호화하는 데 도움이 되는 함수를 제공합니다.