[DRF] Serializer relations

강민성·2024년 9월 21일

DRF API Guide

목록 보기
11/28

Serializer 관계

프로그래밍의 중심은 자료구조이지, 알고리즘이 아니다.
— 롭 파이크

관계 필드는 모델 관계를 표현하는 데 사용됩니다. 이들은 ForeignKey, ManyToManyField, OneToOneField 관계뿐만 아니라, 역참조 관계 및 GenericForeignKey와 같은 사용자 정의 관계에도 적용될 수 있습니다.

참고: 관계 필드는 relations.py에 선언되지만 관례적으로 from rest_framework import serializers를 사용하여 serializers 모듈에서 임포트하고, 필드를 serializers.<FieldName> 형식으로 참조해야 합니다.

참고: REST 프레임워크는 select_relatedprefetch_related 측면에서 serializers에 전달된 queryset을 자동으로 최적화하려고 하지 않습니다. 이는 너무 많은 마법을 요구하기 때문입니다. source 속성을 통해 ORM 관계를 가로지르는 필드를 가진 serializer는 관련 객체를 데이터베이스에서 가져오는 추가적인 데이터베이스 조회를 필요로 할 수 있습니다. 따라서 추가적인 데이터베이스 조회를 피하기 위해 쿼리를 최적화하는 것은 프로그래머의 책임입니다.

예를 들어, 다음 serializer는 tracks 필드를 평가할 때마다 데이터베이스 조회를 발생시킵니다(미리 가져오지 않은 경우):

class AlbumSerializer(serializers.ModelSerializer):
    tracks = serializers.SlugRelatedField(
        many=True,
        read_only=True,
        slug_field='title'
    )

    class Meta:
        model = Album
        fields = ['album_name', 'artist', 'tracks']

# 각 앨범 객체에 대해, tracks는 데이터베이스에서 가져와야 합니다.
qs = Album.objects.all()
print(AlbumSerializer(qs, many=True).data)

AlbumSerializermany=True로 사용되어 꽤 큰 queryset을 직렬화할 경우, 이는 심각한 성능 문제를 초래할 수 있습니다. 다음과 같이 AlbumSerializer에 전달된 queryset을 최적화하면:

qs = Album.objects.prefetch_related('tracks')
# 추가적인 데이터베이스 조회가 필요하지 않습니다.
print(AlbumSerializer(qs, many=True).data)

이 문제를 해결할 수 있습니다.

관계 검사

ModelSerializer 클래스를 사용할 때, serializer 필드와 관계는 자동으로 생성됩니다. 이러한 자동 생성된 필드를 검사하는 것은 관계의 스타일을 정의하는 방법을 결정하는 데 유용한 도구가 될 수 있습니다.

이를 위해 Django 쉘을 열고(python manage.py shell), serializer 클래스를 임포트한 후, 인스턴스를 생성하고 객체 표현을 출력하세요…

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

API 참조

다양한 유형의 관계 필드를 설명하기 위해 간단한 모델 몇 개를 예제로 사용할 것입니다. 우리의 모델은 음악 앨범과 각 앨범에 나열된 트랙을 위한 것입니다.

class Album(models.Model):
    album_name = models.CharField(max_length=100)
    artist = models.CharField(max_length=100)

class Track(models.Model):
    album = models.ForeignKey(Album, related_name='tracks', on_delete=models.CASCADE)
    order = models.IntegerField()
    title = models.CharField(max_length=100)
    duration = models.IntegerField()

    class Meta:
        unique_together = ['album', 'order']
        ordering = ['order']

    def __str__(self):
        return '%d: %s' % (self.order, self.title)

StringRelatedField

StringRelatedField는 관계의 대상을 해당 객체의 __str__ 메서드를 사용하여 표현하는 데 사용할 수 있습니다.

예를 들어, 다음 serializer:

class AlbumSerializer(serializers.ModelSerializer):
    tracks = serializers.StringRelatedField(many=True)

    class Meta:
        model = Album
        fields = ['album_name', 'artist', 'tracks']

다음과 같은 표현으로 직렬화됩니다:

{
    'album_name': 'Things We Lost In The Fire',
    'artist': 'Low',
    'tracks': [
        '1: Sunflower',
        '2: Whitetail',
        '3: Dinosaur Act',
        ...
    ]
}

이 필드는 읽기 전용입니다.

인자:

  • many - 다대다 관계에 적용할 경우 이 인자를 True로 설정해야 합니다.

PrimaryKeyRelatedField

PrimaryKeyRelatedField는 관계의 대상을 기본 키를 사용하여 표현하는 데 사용할 수 있습니다.

예를 들어, 다음 serializer:

class AlbumSerializer(serializers.ModelSerializer):
    tracks = serializers.PrimaryKeyRelatedField(many=True, read_only=True)

    class Meta:
        model = Album
        fields = ['album_name', 'artist', 'tracks']

다음과 같은 표현으로 직렬화됩니다:

{
    'album_name': 'Undun',
    'artist': 'The Roots',
    'tracks': [
        89,
        90,
        91,
        ...
    ]
}

기본적으로 이 필드는 읽기-쓰기입니다. 그러나 read_only 플래그를 사용하여 이 동작을 변경할 수 있습니다.

인자:

  • queryset - 필드 입력을 검증할 때 모델 인스턴스 조회에 사용되는 queryset입니다. 관계는 명시적으로 queryset을 설정하거나 read_only=True로 설정해야 합니다.
  • many - 다대다 관계에 적용할 경우 이 인자를 True로 설정해야 합니다.
  • allow_null - True로 설정하면 필드는 nullable 관계에 대해 None 또는 빈 문자열 값을 허용합니다. 기본값은 False입니다.
  • pk_field - 기본 키 값의 직렬화/역직렬화를 제어하기 위해 필드를 설정합니다. 예를 들어, pk_field=UUIDField(format='hex')는 UUID 기본 키를 압축된 16진수 표현으로 직렬화합니다.

HyperlinkedRelatedField

HyperlinkedRelatedField는 관계의 대상을 하이퍼링크를 사용하여 표현하는 데 사용할 수 있습니다.

예를 들어, 다음 serializer:

class AlbumSerializer(serializers.ModelSerializer):
    tracks = serializers.HyperlinkedRelatedField(
        many=True,
        read_only=True,
        view_name='track-detail'
    )

    class Meta:
        model = Album
        fields = ['album_name', 'artist', 'tracks']

다음과 같은 표현으로 직렬화됩니다:

{
    'album_name': 'Graceland',
    'artist': 'Paul Simon',
    'tracks': [
        'http://www.example.com/api/tracks/45/',
        'http://www.example.com/api/tracks/46/',
        'http://www.example.com/api/tracks/47/',
        ...
    ]
}

기본적으로 이 필드는 읽기-쓰기입니다. 그러나 read_only 플래그를 사용하여 이 동작을 변경할 수 있습니다.

참고: 이 필드는 URL에 단일 URL 키워드 인수를 사용하는 객체를 대상으로 설계되었습니다. 이는 URL의 일부로 단일 기본 키 또는 슬러그 인수를 포함하는 URL에 적합합니다.

더 복잡한 하이퍼링크 표현이 필요한 경우, 아래의 사용자 정의 하이퍼링크 필드 섹션에 설명된 대로 필드를 사용자 정의해야 합니다.

인자:

  • view_name - 관계의 대상을 사용해야 하는 뷰 이름입니다. 표준 라우터 클래스를 사용하는 경우 이 값은 <modelname>-detail 형식의 문자열입니다. 필수입니다.
  • queryset - 필드 입력을 검증할 때 모델 인스턴스 조회에 사용되는 queryset입니다. 관계는 명시적으로 queryset을 설정하거나 read_only=True로 설정해야 합니다.
  • many - 다대다 관계에 적용할 경우 이 인자를 True로 설정해야 합니다.
  • allow_null - True로 설정하면 필드는 nullable 관계에 대해 None 또는 빈 문자열 값을 허용합니다. 기본값은 False입니다.
  • lookup_field - 조회에 사용해야 하는 대상의 필드입니다. 참조된 뷰의 URL 키워드 인수에 해당해야 합니다. 기본값은 'pk'입니다.
  • lookup_url_kwarg - 조회 필드에 해당하는 URL 구성에서 정의된 키워드 인수의 이름입니다. 기본값은 조회 필드와 동일한 값을 사용합니다.
  • format - format 접미사를 사용하는 경우, 하이퍼링크 필드는 기본적으로 동일한 형식 접미사를 사용합니다. 형식 인수를 사용하여 이를 재정의할 수 있습니다.

SlugRelatedField

SlugRelatedField는 관계의 대상을 특정 필드를 사용하여 표현하는 데 사용할 수 있습니다.

예를 들어, 다음 serializer는:

class AlbumSerializer(serializers.ModelSerializer):
    tracks = serializers.SlugRelatedField(
        many=True,
        read_only=True,
        slug_field='title'
    )

    class Meta:
        model = Album
        fields = ['album_name', 'artist', 'tracks']

다음과 같은 표현으로 직렬화됩니다:

{
    'album_name': 'Dear John',
    'artist': 'Loney Dear',
    'tracks': [
        'Airport Surroundings',
        'Everything Turns to You',
        'I Was Only Going Out',
        ...
    ]
}

기본적으로 이 필드는 읽기-쓰기이며, read_only 플래그를 사용하여 이 동작을 변경할 수 있습니다.

SlugRelatedField를 읽기-쓰기 필드로 사용할 때는 일반적으로 slug 필드가 unique=True 속성을 가진 모델 필드에 해당하도록 해야 합니다.

인자:

  • slug_field: 대상을 표현하는 데 사용할 필드입니다. 이는 주어진 인스턴스를 고유하게 식별할 수 있는 필드여야 합니다. 예를 들어, 사용자 이름이 될 수 있습니다. (필수)
  • queryset: 필드 입력을 검증할 때 모델 인스턴스 조회에 사용되는 queryset입니다. 관계는 명시적으로 queryset을 설정하거나 read_only=True로 설정해야 합니다.
  • many: 다대다 관계에 적용할 경우 이 인자를 True로 설정해야 합니다.
  • allow_null: True로 설정하면 필드는 nullable 관계에 대해 None 또는 빈 문자열 값을 허용합니다. 기본값은 False입니다.

HyperlinkedIdentityField

이 필드는 하이퍼링크 모델 serializer의 'url' 필드와 같은 정체성 관계로 적용할 수 있습니다. 또한 객체의 속성으로도 사용할 수 있습니다. 예를 들어, 다음 serializer는:

class AlbumSerializer(serializers.HyperlinkedModelSerializer):
    track_listing = serializers.HyperlinkedIdentityField(view_name='track-list')

    class Meta:
        model = Album
        fields = ['album_name', 'artist', 'track_listing']

다음과 같은 표현으로 직렬화됩니다:

{
    'album_name': 'The Eraser',
    'artist': 'Thom Yorke',
    'track_listing': 'http://www.example.com/api/track_list/12/',
}

이 필드는 항상 읽기 전용입니다.

인자:

  • view_name: 관계의 대상을 사용할 뷰 이름입니다. 표준 라우터 클래스를 사용하는 경우 이 값은 <model_name>-detail 형식의 문자열입니다. (필수)
  • lookup_field: 조회에 사용해야 하는 대상의 필드입니다. 이는 참조된 뷰의 URL 키워드 인수에 해당해야 합니다. 기본값은 'pk'입니다.
  • lookup_url_kwarg: 조회 필드에 해당하는 URL 구성에서 정의된 키워드 인수의 이름입니다. 기본값은 lookup_field와 동일한 값을 사용합니다.
  • format: 형식 접미사를 사용하는 경우, 하이퍼링크 필드는 기본적으로 동일한 형식 접미사를 사용합니다. 형식 인수를 사용하여 이를 재정의할 수 있습니다.

중첩 관계

앞서 논의된 다른 개체에 대한 참조와는 달리, 참조된 개체는 대신에 참조하는 객체의 표현에 임베디드 또는 중첩될 수 있습니다. 이러한 중첩 관계는 필드로서 serializer를 사용하여 표현할 수 있습니다.

필드가 다대다 관계를 표현하는 데 사용되는 경우, serializer 필드에 many=True 플래그를 추가해야 합니다.

예시
다음 serializer를 고려해 보세요:

class TrackSerializer(serializers.ModelSerializer):
    class Meta:
        model = Track
        fields = ['order', 'title', 'duration']

class AlbumSerializer(serializers.ModelSerializer):
    tracks = TrackSerializer(many=True, read_only=True)

    class Meta:
        model = Album
        fields = ['album_name', 'artist', 'tracks']

다음과 같은 중첩 표현으로 직렬화됩니다:

>>> album = Album.objects.create(album_name="The Grey Album", artist='Danger Mouse')
>>> Track.objects.create(album=album, order=1, title='Public Service Announcement', duration=245)
<Track: Track object>
>>> Track.objects.create(album=album, order=2, title='What More Can I Say', duration=264)
<Track: Track object>
>>> Track.objects.create(album=album, order=3, title='Encore', duration=159)
<Track: Track object>
>>> serializer = AlbumSerializer(instance=album)
>>> serializer.data
{
    'album_name': 'The Grey Album',
    'artist': 'Danger Mouse',
    'tracks': [
        {'order': 1, 'title': 'Public Service Announcement', 'duration': 245},
        {'order': 2, 'title': 'What More Can I Say', 'duration': 264},
        {'order': 3, 'title': 'Encore', 'duration': 159},
        ...
    ],
}

쓰기 가능한 중첩 serializer

기본적으로 중첩 serializer는 읽기 전용입니다. 중첩 serializer 필드에 대해 쓰기 작업을 지원하려면, create() 및/또는 update() 메서드를 만들어서 자식 관계가 어떻게 저장되어야 하는지를 명시적으로 지정해야 합니다:

class TrackSerializer(serializers.ModelSerializer):
    class Meta:
        model = Track
        fields = ['order', 'title', 'duration']

class AlbumSerializer(serializers.ModelSerializer):
    tracks = TrackSerializer(many=True)

    class Meta:
        model = Album
        fields = ['album_name', 'artist', 'tracks']

    def create(self, validated_data):
        tracks_data = validated_data.pop('tracks')
        album = Album.objects.create(**validated_data)
        for track_data in tracks_data:
            Track.objects.create(album=album, **track_data)
        return album

다음과 같이 사용할 수 있습니다:

>>> data = {
    'album_name': 'The Grey Album',
    'artist': 'Danger Mouse',
    'tracks': [
        {'order': 1, 'title': 'Public Service Announcement', 'duration': 245},
        {'order': 2, 'title': 'What More Can I Say', 'duration': 264},
        {'order': 3, 'title': 'Encore', 'duration': 159},
    ],
}
>>> serializer = AlbumSerializer(data=data)
>>> serializer.is_valid()
True
>>> serializer.save()
<Album: Album object>

사용자 정의 관계 필드

기존 관계 스타일이 필요로 하는 표현에 맞지 않는 드문 경우, 모델 인스턴스에서 출력 표현이 어떻게 생성되어야 하는지를 정확하게 설명하는 관계 필드를 커스터마이징할 수 있습니다.

사용자 정의 관계 필드를 구현하려면 RelatedField를 오버라이딩하고 .to_representation(self, value) 메서드를 구현해야 합니다. 이 메서드는 필드의 대상을 value 인수로 받아, 대상을 직렬화하는 데 사용해야 하는 표현을 반환해야 합니다. value 인수는 일반적으로 모델 인스턴스입니다.

읽기-쓰기 관계 필드를 구현하려면 .to_internal_value(self, data) 메서드도 구현해야 합니다.

문맥에 따라 동적 queryset을 제공하려면, 필드를 초기화할 때 클래스에서 .queryset을 지정하는 대신 .get_queryset(self)를 오버라이드할 수 있습니다.

예시
예를 들어, 트랙을 주문, 제목 및 지속 시간을 사용하여 사용자 정의 문자열 표현으로 직렬화하는 관계 필드를 정의할 수 있습니다:

import time

class TrackListingField(serializers.RelatedField):
    def to_representation(self, value):
        duration = time.strftime('%M:%S', time.gmtime(value.duration))
        return 'Track %d: %s (%s)' % (value.order, value.title, duration)

class AlbumSerializer(serializers.ModelSerializer):
    tracks = TrackListingField(many=True)

    class Meta:
        model = Album
        fields = ['album_name', 'artist', 'tracks']

이 사용자 정의 필드는 다음과 같은 표현으로 직렬화됩니다:

{
    'album_name': 'Sometimes I Wish We Were an Eagle',
    'artist': 'Bill Callahan',
    'tracks': [
        'Track 1: Jim Cain (04:39)',
        'Track 2: Eid Ma Clack Shaw (04:19)',
        'Track 3: The Wind and the Dove (04:34)',
        ...
    ]
}

사용자 정의 하이퍼링크 필드

어떤 경우에는 하이퍼링크 필드의 동작을 커스텀해야 할 필요가 있습니다. 이는 단일 조회 필드 이상을 요구하는 URL을 표현하기 위함입니다.

이러한 작업은 HyperlinkedRelatedField를 오버라이딩하여 달성할 수 있습니다. 오버라이딩할 수 있는 두 가지 메서드는 다음과 같습니다:

get_url(self, obj, view_name, request, format)

get_url 메서드는 객체 인스턴스를 해당 URL 표현으로 매핑하는 데 사용됩니다.

view_namelookup_field 속성이 URL 설정과 올바르게 일치하지 않으면 NoReverseMatch를 발생시킬 수 있습니다.

get_object(self, view_name, view_args, view_kwargs)

쓰기 가능한 하이퍼링크 필드를 지원하려면, 수신 URL을 해당 객체로 매핑하기 위해 get_object를 오버라이드해야 합니다. 읽기 전용 하이퍼링크 필드의 경우, 이 메서드를 오버라이드할 필요는 없습니다.

이 메서드의 반환 값은 매치된 URL 설정 인수에 해당하는 객체여야 합니다.

ObjectDoesNotExist 예외를 발생시킬 수 있습니다.

예제

고객 객체에 대한 URL이 두 개의 키워드 인수를 사용하는 경우를 생각해 봅시다:

/api/<organization_slug>/customers/<customer_pk>/

이 경우 기본 구현으로는 표현할 수 없습니다. 기본 구현은 단일 조회 필드만 수용하기 때문입니다.

이 경우 원하는 동작을 얻기 위해 HyperlinkedRelatedField를 오버라이드해야 합니다:

from rest_framework import serializers
from rest_framework.reverse import reverse

class CustomerHyperlink(serializers.HyperlinkedRelatedField):
    # 클래스 속성으로 정의하므로 인수로 전달할 필요 없음.
    view_name = 'customer-detail'
    queryset = Customer.objects.all()

    def get_url(self, obj, view_name, request, format):
        url_kwargs = {
            'organization_slug': obj.organization.slug,
            'customer_pk': obj.pk
        }
        return reverse(view_name, kwargs=url_kwargs, request=request, format=format)

    def get_object(self, view_name, view_args, view_kwargs):
        lookup_kwargs = {
           'organization__slug': view_kwargs['organization_slug'],
           'pk': view_kwargs['customer_pk']
        }
        return self.get_queryset().get(**lookup_kwargs)

일반적으로 API 표현을 위해 기본 스타일을 추천하지만, 중첩된 URL 스타일도 적당히 사용할 수 있습니다.

추가적인 참고 사항

queryset 인수

queryset 인수는 쓰기 가능한 관계 필드에서만 필요합니다. 이 경우 원시 사용자 입력을 모델 인스턴스에 매핑하기 위해 모델 인스턴스 조회를 수행하는 데 사용됩니다.

버전 2.x에서는 ModelSerializer 클래스를 사용할 때 자동으로 queryset 인수를 결정할 수 있었습니다. 이제는 쓰기 가능한 관계 필드에 대해 항상 명시적인 queryset 인수를 사용하도록 변경되었습니다.

이렇게 하면 ModelSerializer가 제공하는 숨겨진 '마법'의 양을 줄이고, 필드의 동작을 더 명확하게 하며, ModelSerializer 단축키를 사용하거나 완전하게 명시적인 Serializer 클래스를 사용하는 것 간의 이동이 수월해집니다.

HTML 표시 사용자 정의

모델의 기본 __str__ 메서드는 선택 속성을 채우는 객체의 문자열 표현을 생성하는 데 사용됩니다. 이러한 선택은 탐색 가능한 API의 선택 HTML 입력을 채우는 데 사용됩니다.

이러한 입력에 대한 사용자 정의 표현을 제공하려면, RelatedField 서브클래스의 display_value()를 오버라이드합니다. 이 메서드는 모델 객체를 받아서 그에 적합한 문자열을 반환해야 합니다. 예를 들어:

class TrackPrimaryKeyRelatedField(serializers.PrimaryKeyRelatedField):
    def display_value(self, instance):
        return 'Track: %s' % (instance.title)

선택 필드 중략(cutoff)

탐색 가능한 API에 렌더링될 때 관계 필드는 기본적으로 최대 1000개의 선택 가능한 항목만 표시됩니다. 더 많은 항목이 있을 경우 "1000개 이상의 항목…"이라는 비활성 옵션이 표시됩니다.

이 동작은 너무 많은 관계가 표시되어 템플릿이 수용 가능한 시간 내에 렌더링되지 못하는 것을 방지하기 위한 것입니다.

이 동작을 제어하기 위해 사용할 수 있는 두 가지 키워드 인수가 있습니다:

  • html_cutoff: 설정하면 HTML 선택 드롭다운에 표시될 최대 선택 항목 수가 됩니다. 제한을 해제하려면 None으로 설정합니다. 기본값은 1000입니다.
  • html_cutoff_text: 설정하면 HTML 선택 드롭다운에서 최대 항목 수가 중략(cutoff)된 경우 텍스트 표시기를 표시합니다. 기본값은 "More than {count} items…"입니다.

전역적으로는 HTML_SELECT_CUTOFFHTML_SELECT_CUTOFF_TEXT 설정을 사용하여 이들을 제어할 수 있습니다.

cutoff가 적용되는 경우, HTML 양식에서 일반 입력 필드를 사용하는 것이 좋습니다. 이는 style 키워드 인수를 사용하여 수행할 수 있습니다. 예시:

assigned_to = serializers.SlugRelatedField(
   queryset=User.objects.all(),
   slug_field='username',
   style={'base_template': 'input.html'}
)

역참조 관계

역참조 관계는 ModelSerializerHyperlinkedModelSerializer 클래스에 의해 자동으로 포함되지 않습니다. 역참조 관계를 포함하려면, 반드시 필드 목록에 명시적으로 추가해야 합니다. 예를 들어:

class AlbumSerializer(serializers.ModelSerializer):
    class Meta:
        fields = ['tracks', ...]

관계에 대해 적절한 related_name 인수를 설정했는지 확인하는 것이 일반적입니다. 이 인수를 필드 이름으로 사용할 수 있습니다. 예시:

class Track(models.Model):
    album = models.ForeignKey(Album, related_name='tracks', on_delete=models.CASCADE)

역참조 관계에 대해 related_name을 설정하지 않았다면, 필드 인수에서 자동 생성된 관련 이름을 사용해야 합니다. 예:

class AlbumSerializer(serializers.ModelSerializer):
    class Meta:
        fields = ['track_set', ...]

역참조 관계에 대한 더 자세한 내용은 Django 문서를 참조하세요.

일반 관계

일반 외래 키를 직렬화하려면, 관계의 대상을 직렬화하는 방법을 명시적으로 정의하는 사용자 정의 필드를 정의해야 합니다.

예를 들어, 다른 임의의 모델과 일반 관계를 갖는 태그 모델이 있다고 가정해 보겠습니다:

class TaggedItem(models.Model):
    """
    일반 관계를 사용하여 임의 모델 인스턴스에 태그를 붙입니다.
    
    참조: https://docs.djangoproject.com/en/stable/ref/contrib/contenttypes/
    """
    tag_name = models.SlugField()
    content_type = models.ForeignKey(ContentType, on_delete=models.CASCADE)
    object_id = models.PositiveIntegerField()
    tagged_object = GenericForeignKey('content_type', 'object_id')

    def __str__(self):
        return self.tag_name

그리고 태그와 연결될 수 있는 두 개의 모델이 있습니다:

class Bookmark(models.Model):
    """
    북마크는 URL과 0개 이상의 설명 태그로 구성됩니다.
    """
    url = models.URLField()
    tags = GenericRelation(TaggedItem)


class Note(models.Model):
    """
    노트는 텍스트와 0개 이상의 설명 태그로 구성됩니다.
    """
    text = models.CharField(max_length=1000)
    tags = GenericRelation(TaggedItem)

태그가 붙은 인스턴스를 직렬화하는 데 사용할 수 있는 사용자 정의 필드를 정의할 수 있습니다. 각 인스턴스의 유형을 사용하여 어떻게 직렬화할지를 결정합니다:

class TaggedObjectRelatedField(serializers.RelatedField):
    """
    `tagged_object` 일반 관계에 사용할 사용자 정의 필드입니다.
    """

    def to_representation(self, value):
        """
        태그가 붙은 객체를 단순한 텍스트 표현으로 직렬화합니다.
        """
        if isinstance(value, Bookmark):
            return 'Bookmark: ' + value.url
        elif isinstance(value, Note):
            return 'Note: ' + value.text
        raise Exception('예상치 못한 유형의 태그가 붙은 객체입니다.')

관계의 대상을 중첩 표현으로 나타내고 싶다면, .to_representation() 메서드 내에서 필요한 직렬화를 사용할 수 있습니다:

def to_representation(self, value):
    """
    북마크 인스턴스는 북마크 직렬화를 사용하여,
    노트 인스턴스는 노트 직렬화를 사용하여 직렬화합니다.
    """
    if isinstance(value, Bookmark):
        serializer = BookmarkSerializer(value)
    elif isinstance(value, Note):
        serializer = NoteSerializer(value)
    else:
        raise Exception('예상치 못한 유형의 태그가 붙은 객체입니다.')

    return serializer.data

GenericRelation 필드를 사용하여 표현된 역 일반 키는 관계의 대상 유형이 항상 알려져 있기 때문에 일반 관계 필드 유형을 사용하여 직렬화할 수 있습니다.

더 자세한 정보는 Django의 일반 관계에 대한 문서를 참조하세요.

Through 모델을 사용하는 ManyToManyFields

기본적으로, 지정된 Through 모델을 목표로 하는 관계 필드는 읽기 전용으로 설정됩니다.

Through 모델을 가리키는 관계 필드를 명시적으로 지정하는 경우, read_onlyTrue로 설정해야 합니다.

Through 모델에서 추가 필드를 나타내고 싶다면, Through 모델을 중첩 객체로 직렬화할 수 있습니다.

외부 패키지

다음 외부 패키지도 사용 가능합니다.

DRF Nested Routers

drf-nested-routers 패키지는 중첩 리소스를 다루기 위한 라우터와 관계 필드를 제공합니다.

Rest Framework Generic Relations

rest-framework-generic-relations 라이브러리는 일반 외래 키에 대한 읽기/쓰기가 가능한 직렬화를 제공합니다.

Reference

DRF API Guide - Serializer relations

profile
Back-end Junior Developer

0개의 댓글