DRF: Serializers

hwisaac·2023년 8월 23일
0

drf

목록 보기
1/3

시리얼라이저

시리얼라이저의 유용성을 확장하는 것은 우리가 다루고 싶어하는 주제입니다. 그러나 이것은 간단한 문제가 아니며, 심각한 설계 작업이 필요할 것입니다.

— Russell Keith-Magee, Django 사용자 그룹

시리얼라이저는 쿼리셋과 모델 인스턴스와 같은 복잡한 데이터를 원시 Python 데이터 유형으로 변환하여 이를 간단하게 JSON, XML 또는 다른 콘텐츠 유형으로 렌더링할 수 있도록 해줍니다. 시리얼라이저는 역직렬화도 제공하여 구문 분석된 데이터를 먼저 유효성 검사한 후에 복잡한 유형으로 다시 변환할 수 있게 해줍니다.

REST 프레임워크의 시리얼라이저는 Django의 FormModelForm 클래스와 매우 유사하게 작동합니다. 우리는 출력 응답을 효과적으로 제어하는 강력하고 범용적인 방법을 제공하는 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()에 추가 속성 전달

가끔 보기 코드에서 인스턴스를 저장하는 시점에 추가 데이터를 주입할 수 있도록 하려는 경우가 있습니다. 이 추가 데이터에는 현재 사용자, 현재 시간 또는 요청 데이터의 일부가 아닌 다른 어떤 것이든 포함될 수 있습니다.

.save()를 호출할 때 추가 키워드 인수를 포함하여 이를 수행할 수 있습니다. 예를 들어:

serializer.save(owner=request.user)

추가 키워드 인수는 .create() 또는 .update()가 호출될 때 validated_data 인수에 포함됩니다.

.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)

위의 경우에서는 이제 직접 시리얼라이저의 .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']
            )
        ]

자세한 정보는 유효성 검사기 문서를 참조하세요.

Accessing the initial data and instance

시리얼라이저 인스턴스에 초기 객체나 쿼리셋을 전달할 때 해당 객체는 .instance으로 사용할 수 있습니다. 초기 객체가 전달되지 않으면 .instance 속성은 None이 됩니다.

시리얼라이저 인스턴스에 데이터를 전달할 때 수정되지 않은 데이터는 .initial_data로 사용할 수 있습니다. data 키워드 인수가 전달되지 않으면 .initial_data 속성은 존재하지 않습니다.

Partial updates

기본적으로 필수 필드에 대해 값이 전달되지 않으면 시리얼라이저가 유효성 검사 오류를 발생시킵니다. 부분 업데이트를 허용하려면 partial 인수를 사용할 수 있습니다.

# 'content' 필드를 이용해 'comment'를 부분 데이터로 업데이트합니다.
serializer = CommentSerializer(comment, data={'content': 'foo bar'}, partial=True)

Dealing with nested objects

이전 예제는 단순한 데이터 유형만 갖는 객체를 다루는 데 적합하지만, 때로는 더 복잡한 객체를 나타내야 할 때도 있습니다. 객체의 일부 속성이 문자열, 날짜 또는 정수와 같은 단순한 데이터 유형이 아닐 수 있는 복잡한 객체를 표현해야 할 때도 있습니다.

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()

Writable nested representations

중첩된 표현을 처리하는 동안 데이터를 역직렬화할 수 있는 경우, 중첩된 객체와 관련된 모든 오류는 중첩된 객체의 필드 이름 아래에 중첩됩니다.

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 속성을 통해 사용

할 수 있습니다.

ModelSerializer

자주 사용되는 시리얼라이저 클래스는 Django 모델 정의와 밀접하게 매핑되는 시리얼라이저 클래스를 원할 것입니다.

ModelSerializer 클래스는 다음과 같은 특징을 가지며 일반적인 Serializer 클래스와 동일합니다.

  • 모델에 기반하여 필드 세트를 자동으로 생성합니다.
  • 유효성 검사자를 자동으로 생성합니다(고유한 필드와 관련된).
  • .create().update()의 간단한 기본 구현을 포함합니다.

ModelSerializer를 선언하는 것은 다음과 같습니다.

class AccountSerializer(serializers.ModelSerializer):
    class Meta:
        model = Account
        fields = ['id', 'account_name', 'users', 'created']

기본적으로 클래스의 모든 모델 필드는 해당하는 시리얼라이저 필드에 매핑됩니다.

모델에서 외래 키와 같은 관계가 있다면 이는 PrimaryKeyRelatedField로 매핑됩니다. 역방향 관계는 기본적으로 포함되지 않지만 serializer relations 문서에서 명시적으로 포함되도록 지정된 경우에만 포함됩니다.

ModelSerializer 검사

시리얼라이저 클래스는 상태를 완전히 검사할 수 있게 해주는 유용한 상세 표현 문자열을 생성합니다. 특히 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_namecreated 필드만 직렬화될 것입니다.

fieldsexclude 속성의 이름은 일반적으로 모델 클래스의 모델 필드와 매핑됩니다.

또는 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=Truedefault=... 키워드 인수를 함께 제공하는 것이 올바른 방법입니다.

이와 관련한 한 가지 예는 현재 인증된 User와 다른 식별자와 함께 unique_together인 읽기 전용 관계입니다. 이 경우 사용자 필드는 다음과 같이 선언할 수 있습니다:

user = serializers.PrimaryKeyRelatedField(read_only=True, default=serializers.CurrentUserDefault())

[Validators 문서](https://www.django-rest-framework.org/api-guide/validators/)를 검토하여 UniqueTogetherValidatorCurrentUserDefault 클래스에 대한 자세한 내용을 확인하십시오.

추가 키워드 인수

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 프레임워크 직렬라이저 필드로 매핑하는 맵입니다. 이 매핑을 재정의하여 각 모델 필드에 대해 사용할 기본 직렬라이저 필드를 변경할 수 있습니다.

기본적으로 관계형 필드에 대해 사용되는 직렬라이저 필드 클래스입니다.

ModelSerializer의 경우 기본값은 serializers.PrimaryKeyRelatedField입니다.

HyperlinkedModelSerializer의 경우 기본값은 serializers.HyperlinkedRelatedField입니다.

.serializer_url_field

직렬라이저의 url 필드에 사용될 직렬라이저 필드 클래스입니다.

기본값은 serializers.HyperlinkedIdentityField입니다.

.serializer_choice_field

직렬라이저의 선택 필드에 사용될 직렬라이저 필드 클래스입니다.

기본값은 serializers.ChoiceField입니다.

field_class 및 field_kwargs API

각 필드의 클래스와 키워드 인수를 결정하기 위해 호출되는 다음 메서드는 각각 두 개의 튜플인 (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_depthdepth 옵션의 값에서 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

HyperlinkedModelSerializer 클래스는 ModelSerializer 클래스와 유사하지만 관계를 나타내는 데 기본 키 대신 하이퍼링크를 사용합니다.

기본적으로 직렬라이저는 기본 키 필드 대신 url 필드를 포함합니다.

url 필드는 HyperlinkedIdentityField 직렬라이저 필드를 사용하여 나타내며, 모델의 관계는 HyperlinkedRelatedField 직렬라이저 필드를 사용하여 나타냅니다.

fields 옵션에 기본 키를 명시적으로 포함하려면 다음과

같이 수행할 수 있습니다:

class AccountSerializer(serializers.HyperlinkedModelSerializer):
    class Meta:
        model = Account
        fields = ['url', 'id', 'account_name', 'users', 'created']

절대 및 상대 URL

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_namelookup_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'입니다. 이 이름을 전역적으로 변경하려면 URL_FIELD_NAME 설정을 사용할 수 있습니다.

ListSerializer

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의 ModelModelForm 클래스와 유사하게, 직렬라이저의 내부 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자 패키지

다음 제3자 패키지도 사용 가능합니다.

Django REST marshmallow

django-rest-marshmallow 패키지는 파이썬 marshmallow 라이브러리를 사용한 대안적인 직렬라이저 구현을 제공합니다. 이 패키지는 REST framework 직렬라이저와 동일한 API를 노출하며, 특정 사용 사례에서는 이를 대체로 사용할 수 있습니다.

Serpy

serpy 패키지는 속도를 위해 구축된 대안적인 직렬라이저 구현입니다. Serpy는 복잡한 데이터 유형을 간단한 네이티브 유형으로 직렬화합니다. 이 네이티브 유형은 JSON이나 다른 필요한 형식으로 쉽게 변환될 수 있습니다.

MongoengineModelSerializer

django-rest-framework-mongoengine 패키지는 MongoEngineModelSerializer 직렬라이저 클래스를 제공하며, Django REST framework에서 MongoDB를 저장소 계층으로 사용하는 데 사용할 수 있습니다.

GeoFeatureModelSerializer

django-rest-framework-gis 패키지는 GeoFeatureModelSerializer 직렬라이저 클래스를 제공하며, GeoJSON을 읽고 쓰는 데 모두 사용할 수 있습니다.

HStoreSerializer

django-rest-framework-hstore 패키지는 HStoreSerializer를 제공하여 django-hstoreDictionaryField 모델 필드와 해당 schema-mode 기능을 지원합니다.

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하게 유지하기 위한 도구 모음을 제공합니다. 필드를 뷰/요청 기반으로 정의할 수 있도록 하여 필드를 화이트리스트 또는 블랙리스트로 설정하고 하위 직렬라이저를 선택적으로 확장할 수 있습니다.

HTML JSON Forms

html-json-forms 패키지는 (비활성화된) HTML JSON Form 사양에 따라 `<

form>제출을 처리하기 위한 알고리즘과 직렬라이저를 제공합니다. 이 직렬라이저는 HTML 내에서 임의로 중첩된 JSON 구조를 처리하는 데 도움이 됩니다. 예를 들어,{"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를 통해 직렬화된 데이터를 암호화하는 데 도움이 되며, 몇 가지 보조 함수도 포함되어 있습니다. 데이터를 암호화하는 데 도움이 되는 함수를 제공합니다.

0개의 댓글

Powered by GraphCDN, the GraphQL CDN