Django REST Framework/DRF 일반

[DRF] 공식 문서 - serializer relations 정리

bluebamus 2024. 1. 15.

 1. Serializer relations

   1) 정의

      - 관계형 필드는 모델 간의 관계를 나타내는 데 사용된다. 

      - 이들은 ForeignKey, ManyToManyField 및 OneToOneField 관계뿐만 아니라 역 관계 및 GenericForeignKey와 같은 사용자 정의 관계에도 적용될 수 있다.

 


      - 참고: 관계형 필드는 relations.py에서 선언되지만, 관례적으로 serializers 모듈에서 임포트하여 사용해야 한다. 

         - 즉, from rest_framework import serializers를 사용하고 필드를 serializers.<FieldName>로 참조해야 한다.


      - 참고: REST 프레임워크는 select_related와 prefetch_related에 대해 시리얼라이저로 전달된 쿼리셋을 자동으로 최적화하려는 시도를 하지 않는다. 

         - 이유는 이것이 과도한 자동화가 될 수 있기 때문이다.

         - source 속성을 통해 ORM 관계를 연결하는 필드가 있는 시리얼라이저는 데이터베이스에서 관련 객체를 가져오는데 추가적인 데이터베이스 접근이 필요할 수 있다.

         - 이런 시리얼라이저를 사용하면서 발생할 수 있는 추가적인 데이터베이스 접근을 피하기 위해 쿼리를 최적화하는 것은 프로그래머의 책임이다.


      - 예를 들어, 아래의 시리얼라이저는 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']

# For each album object, tracks should be fetched from database
qs = Album.objects.all()
print(AlbumSerializer(qs, many=True).data)

 


      - SlugRelatedField는 Django REST framework의 시리얼라이저 필드 중 하나로, 관계형 필드를 대표하는 슬러그(slug) 값을 사용하여 관련 객체를 나타낸다. 

      - 슬러그 값은 slug_field 인자로 지정된 필드의 값이다.

         - 이 필드는 관련 객체를 그 객체의 특정 필드 값으로 간편하게 나타낼 수 있도록 해준다.

      - 예를 들어, Track 객체를 시리얼라이즈할 때 Track 객체 전체를 표현하는 대신 title 필드만을 사용하여 Track 객체를 표현하고 싶다면, SlugRelatedField(slug_field='title')를 사용할 수 있다.

      - 다른 관계형 필드인 PrimaryKeyRelatedField는 관련 객체를 그 객체의 기본 키 값으로 나타낸다.

         - 이 필드를 사용하면, Track 객체는 그 객체의 기본 키 값으로 나타나게 된다.

      - 따라서 SlugRelatedField를 사용하는 이유는, 각 Track 객체를 그 객체의 기본 키 값이 아닌 title 필드 값으로 나타내고 싶기 때문이다.

         - 이는 클라이언트에게 더 읽기 쉬운 정보를 제공하며, 특히 Track 객체의 제목이 클라이언트에게 중요한 정보일 경우 유용하다.

      - SlugRelatedField는 다음과 같은 경우에 주로 사용된다:

      - 관련 객체를 그 객체의 특정 필드 값으로 나타내고 싶은 경우. 예를 들어, Track 객체를 그 객체의 title 필드 값으로 나타내고 싶다면, SlugRelatedField(slug_field='title')를 사용할 수 있다.
      - 관련 객체의 전체 정보를 제공하는 것이 아니라, 관련 객체의 특정 필드 값만을 제공하고 싶은 경우. 예를 들어, Track 객체의 전체 정보를 제공하는 것이 아니라 Track 객체의 제목만을 제공하고 싶다면, SlugRelatedField(slug_field='title')를 사용할 수 있다.
      - 관련 객체를 그 객체의 기본 키 값이 아닌 다른 필드 값으로 나타내고 싶은 경우. 예를 들어, Track 객체를 그 객체의 기본 키 값이 아닌 title 필드 값으로 나타내고 싶다면, SlugRelatedField(slug_field='title')를 사용할 수 있다.


 

      - 만약 AlbumSerializer가 many=True로 설정된 상태에서 꽤 큰 쿼리셋을 시리얼라이즈하는데 사용된다면, 이는 심각한 성능 문제를 초래할 수 있다.

      - AlbumSerializer에 전달된 쿼리셋을 최적화하는 것이 필요하다.

qs = Album.objects.prefetch_related('tracks')
# No additional database hits required
print(AlbumSerializer(qs, many=True).data)

 

      - 이 문제를 해결할 수 있다.

      - 관계 검사(Inspecting relationships)

         - ModelSerializer 클래스를 사용하면, 시리얼라이저 필드와 관계는 자동으로 생성된다. 

         - 이러한 자동 생성된 필드를 검사하는 것은 관계 스타일을 어떻게 커스터마이징할지 결정하는데 유용한 도구가 될 수 있다.

          - 이를 하려면, Django 쉘을 열고 python manage.py shell을 사용한 후, 시리얼라이저 클래스를 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. API Reference

   - 다양한 종류의 관계형 필드를 설명하기 위해, 몇 가지 간단한 예시를 위한 모델을 사용한다.

   - 우리의 모델은 음악 앨범과 각 앨범에 등록된 트랙들에 대한 것이다.

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)

 

   1) StringRelatedField

      - 이 필드는 읽기 전용이다.

      - 하지만 StringRelatedField를 쓰기 가능하게 만들고 싶다면, to_internal_value(self, data) 메서드를 오버라이딩하여 구현할 수 있다.

      - 이 메서드는 데이터를 내부 타입으로 변환하는 역할을 한다.

      - 이렇게 하면 StringRelatedField를 통해 데이터를 업데이트하거나 생성하는 것이 가능해진다.

      - 그러나 일반적으로는 StringRelatedField를 읽기 전용으로 사용하고, 쓰기 작업은 다른 필드 타입을 사용하는 것이 권장된다.

      - 이는 StringRelatedField의 주요 목적이 관계된 객체의 __str__ 표현을 제공하는 것이기 때문이다.

      - StringRelatedField는 대상의 관계를 그것의 __str__ 메서드를 사용해 나타내는데 사용할 수 있다.

      - 예를 들어, 다음과 같은 시리얼라이저를 보면,

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',
        ...
    ]
}

 

      - Arguments

         - many - 만약 이것이 to-many 관계에 적용된다면, 이 인자를 True로 설정해야 한다.

 

   2) PrimaryKeyRelatedField

      - PrimaryKeyRelatedField는 관계의 대상을 그것의 기본 키를 사용하여 나타내는 데 사용될 수 있다.

      - 예를 들어, 다음과 같은 시리얼라이저를 보면,

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 플래그를 사용해 이러한 동작을 변경할 수 있다.

      - Arguments:

         - queryset - 필드 입력을 검증할 때 모델 인스턴스 조회에 사용되는 쿼리셋이다.

            - 관계는 쿼리셋을 명시적으로 설정하거나, read_only=True를 설정해야 한다.
         - many - 만약 이것이 to-many 관계에 적용된다면, 이 인자를 True로 설정해야 한다.
         - allow_null - True로 설정하면, 이 필드는 None 값이나 빈 문자열을 허용하는 관계에 대해 허용한다.

            - 기본값은 False이다.
         - pk_field - 기본 키의 값의 직렬화/역직렬화를 제어하기 위해 필드를 설정한다.

            - 예를 들어, pk_field=UUIDField(format='hex')는 UUID 기본 키를 그것의 간결한 헥스 표현으로 직렬화한다.

 

   3) HyperlinkedRelatedField

      - HyperlinkedRelatedField는 하이퍼링크를 사용하여 관계의 대상을 나타내는 데 사용될 수 있다.

      - 예를 들어, 다음과 같은 시리얼라이저를 보면,

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 플래그를 사용해 이러한 동작을 변경할 수 있다.


         - 참고: 이 필드는 lookup_field과 lookup_url_kwarg 인자를 사용하여 설정한 단일 URL 키워드 인자를 받는 URL에 매핑되는 객체를 위해 설계되었다.

         - 이는 URL의 일부로 단일 기본 키나 슬러그 인자를 포함하는 URL에 적합하다.

         - 더 복잡한 하이퍼링크 표현이 필요한 경우, 아래의 사용자 정의 하이퍼링크 필드 섹션에서 설명된 바와 같이 필드를 커스텀해야 한다.


         - Arguments:

            - view_name - 관계의 대상으로 사용되어야 하는 뷰 이름이다. 표준 라우터 클래스를 사용하고 있다면 이것은 <modelname>-detail 형식의 문자열이 될 것이다. 필수이다.

            - queryset - 필드 입력을 검증할 때 모델 인스턴스 조회에 사용되는 쿼리셋이다. 관계는 쿼리셋을 명시적으로 설정하거나, read_only=True를 설정해야 한다.

            - many - 만약 이것이 to-many 관계에 적용된다면, 이 인자를 True로 설정해야 한다.

            - allow_null - True로 설정하면, 이 필드는 None 값이나 빈 문자열을 허용하는 관계에 대해 허용한다. 기본값은 False이다.

            - lookup_field - 대상에서 조회에 사용되어야 하는 필드이다. 참조된 뷰의 URL 키워드 인자에 해당해야 한다. 기본값은 'pk'이다.

            - lookup_url_kwarg - URL conf에 정의된 키워드 인자의 이름으로, 조회 필드에 해당한다. 기본적으로는 lookup_field와 같은 값을 사용한다.

            - format - 형식 접미사를 사용하는 경우, 하이퍼링크 필드는 format 인자를 사용하여 재정의하지 않는 한, 대상에 대해 같은 형식 접미사를 사용하게 된다.

 

   4) SlugRelatedField

      - SlugRelatedField는 대상의 필드를 사용하여 관계의 대상을 나타내는 데 사용될 수 있다.

      - 예를 들어, 다음과 같은 시리얼라이저를 보면,

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를 읽기-쓰기 필드로 사용할 때, 일반적으로 슬러그 필드가 unique=True 속성을 가진 모델 필드와 일치하도록 보장하고 싶을 것이다.

      - Arguments:

         - slug_field - 대상을 나타내는 데 사용되어야 하는 대상의 필드이다. 이는 주어진 인스턴스를 고유하게 식별하는 필드여야 한다. 예를 들어, 사용자 이름이 이에 해당한다. 필수이다.

         - queryset - 필드 입력을 검증할 때 모델 인스턴스 조회에 사용되는 쿼리셋이다. 관계는 쿼리셋을 명시적으로 설정하거나, read_only=True를 설정해야 한다.

         - many - 만약 이것이 to-many 관계에 적용된다면, 이 인자를 True로 설정해야 한다.

         - allow_null - True로 설정하면, 이 필드는 None 값이나 빈 문자열을 허용하는 관계에 대해 허용한다. 기본값은 False이다.

 

   5) HyperlinkedIdentityField

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

      - 이 필드는 HyperlinkedModelSerializer의 'url' 필드와 같은 식별 관계로 적용될 수 있다.

      - 또한 객체의 속성에 대해 사용될 수도 있다.

      - 예를 들어, 다음과 같은 시리얼라이저를 보면,

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/',
}

 

      - Arguments:

         - view_name - 관계의 대상으로 사용되어야 하는 뷰 이름이다. 표준 라우터 클래스를 사용하고 있다면, 이것은 <model_name>-detail 형식의 문자열이 될 것이다. 필수 항목이다.

         - lookup_field - 조회를 위해 대상에서 사용되어야 하는 필드이다. 참조된 뷰에서 URL 키워드 인자와 일치해야 한다. 기본값은 'pk'이다.

         - lookup_url_kwarg - URL 설정에서 정의된 키워드 인자의 이름으로, 조회 필드와 일치해야 한다. 기본적으로 lookup_field와 같은 값을 사용하도록 설정되어 있다.

         - format - 형식 접미사를 사용하는 경우, 하이퍼링크 필드는 format 인자를 사용하여 재정의하지 않는 한, 대상에 대해 같은 형식 접미사를 사용하게 된다.

 

 3. Nested relationships

   1) 정의

      - 이전에 논의된 다른 엔티티에 대한 참조와 달리, 참조된 엔티티는 참조하는 객체의 표현 내에 임베디드되거나 중첩될 수도 있다.

      - 이러한 중첩된 관계는 필드로서의 시리얼라이저를 사용함으로써 표현될 수 있다.

      - 필드가 to-many 관계를 표현하는 데 사용되는 경우, 시리얼라이저 필드에 many=True 플래그를 추가해야 한다.

 

   2) Example

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},
        ...
    ],
}

 

   3) Writable nested serializers

      - 기본적으로 중첩된 시리얼라이저는 읽기 전용이다.

      - 중첩된 시리얼라이저 필드에 쓰기 연산을 지원하려면, 자식 관계가 어떻게 저장되어야 하는지 명확히 지정하기 위해 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>

 

 4. Custom relational fields

   1) 정의

      - 기존의 관계 스타일이 필요한 표현을 충족시키지 못하는 특수한 경우에, 모델 인스턴스에서 출력 표현이 어떻게 생성되어야 하는지를 정확하게 설명하는 완전히 사용자 정의된 관계 필드를 구현할 수 있다.

      - 사용자 정의 관계 필드를 구현하기 위해선, RelatedField를 재정의하고, .to_representation(self, value) 메서드를 구현해야 한다.

      - 이 메서드는 필드의 대상을 value 인자로 받아, 대상을 직렬화하는데 사용되어야 하는 표현을 반환해야 한다.

      - value 인자는 대부분의 경우 모델 인스턴스일 것입이다.

      - 읽기와 쓰기가 가능한 관계 필드를 구현하려면, .to_internal_value(self, data) 메서드를 추가로 구현해야 한다.

      - 맥락에 기반한 동적 쿼리셋을 제공하려면, 클래스에 .queryset을 지정하거나 필드를 초기화할 때 .queryset을 사용하는 대신 .get_queryset(self)를 재정의할 수도 있다.

 

   2) Example

      - 예를 들어, 우리는 트랙의 순서, 제목, 그리고 지속 시간을 사용하여 사용자 정의 문자열 표현으로 시리얼라이즈하기 위한 관계 필드를 정의할 수 있다.

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.name, 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)',
        ...
    ]
}

 

 5. Custom hyperlinked fields

   1) 정의

      - 일부 경우에서 단일 조회 필드보다 더 많은 정보를 요구하는 URL을 표현하기 위해 하이퍼링크 필드의 동작을 사용자 정의해야 할 수 있다. 이를 위해 HyperlinkedRelatedField를 재정의할 수 있다. 재정의할 수 있는 두 가지 메서드는 다음과 같다: 

 

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

         - get_url 메서드는 객체 인스턴스를 그것의 URL 표현으로 매핑하는 데 사용된다. view_name과 lookup_field 속성이 URL conf와 올바르게 매치되도록 설정되지 않았다면 NoReverseMatch를 발생시킬 수 있다. 

 

      - get_object(self, view_name, view_args, view_kwargs) :

         - 쓰기 가능한 하이퍼링크 필드를 지원하려면 들어오는 URL을 그것이 표현하는 객체로 다시 매핑하기 위해 get_object를 재정의하고 싶을 것이다. 읽기 전용 하이퍼링크 필드의 경우 이 메서드를 재정의할 필요가 없다. 

 

      - 이 메서드의 반환값은 매치된 URL conf 인자에 해당하는 객체여야 한다. 

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

 

   2) Example

      - 우리가 고객 객체에 대한 URL을 가지고 있고, 이 URL이 두 개의 키워드 인자를 받는다고 가정해보면, 다음과 같을 수 있다.

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

 

      - 이것은 단일 조회 필드만을 받아들이는 기본 구현으로는 표현할 수 없다. 이 경우에는 우리가 원하는 동작을 얻기 위해 HyperlinkedRelatedField를 오버라이딩해야 한다.

from rest_framework import serializers
from rest_framework.reverse import reverse

class CustomerHyperlink(serializers.HyperlinkedRelatedField):
    # We define these as class attributes, so we don't need to pass them as arguments.
    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)

 

      - 이 스타일을 제네릭 뷰와 함께 사용하려면, 올바른 조회 동작을 얻기 위해 뷰에서 .get_object를 재정의해야 한다는 점을 주의해야 한다. 

      - 일반적으로 API 표현에는 가능한 한 평평한 스타일을 추천하지만, 적절하게 사용되면 중첩된 URL 스타일도 합리적일 수 있다.

 

 6. Further notes

   1) The queryset argument

      - queryset 인자는 쓰기 가능한 관계 필드에 대해서만 필요하며, 이 경우에는 원시 사용자 입력에서 모델 인스턴스로의 매핑을 수행하는 데 사용된다.

      - 2.x 버전에서는 ModelSerializer 클래스가 사용되고 있다면 직렬화 클래스가 queryset 인자를 자동으로 결정할 수 있었다.

      - 이 동작은 이제 쓰기 가능한 관계 필드에 대해 항상 명시적인 queryset 인자를 사용하는 것으로 대체되었다.

      - 이렇게 하면 ModelSerializer가 제공하는 숨겨진 '마법'이 줄어들고, 필드의 동작이 더욱 명확해지며, ModelSerializer의 단축키를 사용하거나 완전히 명시적인 Serializer 클래스를 사용하는 것 사이에서 쉽게 전환할 수 있게 된다.

 

   2) Customizing the HTML display

      - 모델의 기본 str 메서드는 choices 속성을 채우는 데 사용되는 객체들의 문자열 표현을 생성하는 데 사용된다.

      - 이러한 선택사항들은 브라우저에서 볼 수 있는 API에서 select HTML 입력을 채우는 데 쓰인다.

      - 이런 입력에 대해 사용자 정의 표현을 제공하려면, RelatedField 서브클래스의 display_value()를 재정의 해야한다.

      - 이 메서드는 모델 객체를 인자로 받고, 그 객체를 표현하는 데 적절한 문자열을 반환해야 한다. 

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

 

   3) Select field cutoffs

      - 브라우저에서 볼 수 있는 API에서 렌더링될 때, 관계형 필드는 기본적으로 최대 1000개의 선택 가능한 아이템만을 표시하도록 설정된다. 만약 더 많은 아이템이 존재한다면 "1000개 이상의 아이템…"이라는 표시가 비활성화된 옵션으로 나타난다.

      - 이런 동작은 매우 많은 수의 관계가 표시됨으로써 템플릿이 적절한 시간 내에 렌더링되지 못하는 것을 방지하기 위한 것이다.

      - 이런 동작을 조절하는 두 가지 키워드 인자가 있다:
         - html_cutoff - 설정하면 이것은 HTML select 드롭다운에서 표시될 최대 선택 항목 수를 의미합니다. 제한을 끄려면 None으로 설정하면 된다. 기본값은 1000이다.
         - html_cutoff_text - 설정하면 HTML select 드롭다운에서 최대 아이템 수가 제한되었음을 나타내는 텍스트를 표시한다. 기본값은 "More than {count} items…"이다.


      - HTML_SELECT_CUTOFF 및 HTML_SELECT_CUTOFF_TEXT 설정을 통해 이들을 전역적으로 조절할 수도 있다.
      - 제한이 적용되고 있는 상황에서는 HTML 폼에서 일반 입력 필드를 사용하고 싶을 수 있다. 이럴 때는 style 키워드 인자를 사용하면 된다.

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

 

   4) Reverse relations

      - ModelSerializer와 HyperlinkedModelSerializer 클래스에서는 역방향 관계가 자동으로 포함되지 않는다는 점을 주의해야 한다. 역방향 관계를 포함시키려면, 반드시 필드 목록에 명시적으로 추가해야 한다.

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을 설정하지 않았다면, fields 인자에서 자동 생성된 related name을 사용해야 한다.

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

 

      - 더 자세한 내용은 장고 문서의 역방향 관계 부분을 참조하면 된다.

         - https://docs.djangoproject.com/en/5.0/topics/db/queries/#following-relationships-backward

 

Django

The web framework for perfectionists with deadlines.

docs.djangoproject.com

 

   5) Generic relationships

      - 일반 외래 키를 직렬화하려면, 관계의 대상을 어떻게 직렬화할지 명확하게 결정하기 위해 사용자 정의 필드를 정의해야 한다.

      - 예를 들어, 다른 임의의 모델과 일반적인 관계를 가진 태그에 대한 다음 모델이 주어졌다고 가정해보자.

class TaggedItem(models.Model):
    """
    Tags arbitrary model instances using a generic relation.

    See: 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):
    """
    A bookmark consists of a URL, and 0 or more descriptive tags.
    """
    url = models.URLField()
    tags = GenericRelation(TaggedItem)


class Note(models.Model):
    """
    A note consists of some text, and 0 or more descriptive tags.
    """
    text = models.CharField(max_length=1000)
    tags = GenericRelation(TaggedItem)

 

      - 각 인스턴스의 유형을 사용하여 어떻게 직렬화해야 하는지 결정하면서, 태그가 붙은 인스턴스를 직렬화하는 데 사용할 수 있는 사용자 정의 필드를 정의할 수 있다.

class TaggedObjectRelatedField(serializers.RelatedField):
    """
    A custom field to use for the `tagged_object` generic relationship.
    """

    def to_representation(self, value):
        """
        Serialize tagged objects to a simple textual representation.
        """
        if isinstance(value, Bookmark):
            return 'Bookmark: ' + value.url
        elif isinstance(value, Note):
            return 'Note: ' + value.text
        raise Exception('Unexpected type of tagged object')

 

      - 관계의 대상이 중첩된 표현을 가지도록 하려면, .to_representation() 메소드 내부에서 필요한 serializers를 사용할 수 있다.

    def to_representation(self, value):
        """
        Serialize bookmark instances using a bookmark serializer,
        and note instances using a note serializer.
        """
        if isinstance(value, Bookmark):
            serializer = BookmarkSerializer(value)
        elif isinstance(value, Note):
            serializer = NoteSerializer(value)
        else:
            raise Exception('Unexpected type of tagged object')

        return serializer.data

 

      - GenericRelation 필드를 사용하여 표현되는 역방향 일반 키는, 관계에서 대상의 유형이 항상 알려져 있기 때문에 일반 관계형 필드 유형을 사용하여 직렬화할 수 있다는 점을 주의해야 한다.

      - 자세한 내용은 장고 문서의 일반 관계 부분을 참조하면 된다.

         - https://docs.djangoproject.com/en/5.0/ref/contrib/contenttypes/#id1

 

Django

The web framework for perfectionists with deadlines.

docs.djangoproject.com

 

   6) ManyToManyFields with a Through Model

      - 기본적으로, through 모델이 지정된 ManyToManyField를 대상으로 하는 관계형 필드는 읽기 전용으로 설정된다.
      - through 모델이 있는 ManyToManyField를 가리키는 관계형 필드를 명시적으로 지정한다면, 반드시 read_only를 True로 설정해야 한다.
      - through 모델에 추가 필드를 표현하려면, through 모델을 중첩된 객체로 직렬화할 수 있습니다.

         - extra fields on a through model : https://docs.djangoproject.com/en/5.0/topics/db/models/#intermediary-manytomany

         - a nested object : https://www.django-rest-framework.org/api-guide/serializers/#dealing-with-nested-objects

 

 7. Third Party Packages

   1) DRF Nested Routers

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

      - 링크 : https://github.com/alanjds/drf-nested-routers

   2) Rest Framework Generic Relations

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

      - 링크 : https://github.com/LilyFoote/rest-framework-generic-relations

 

 - 공식 사이트 문서 : https://www.django-rest-framework.org/api-guide/relations/#serializer-relations

 

Serializer relations - Django REST framework

relations.py Data structures, not algorithms, are central to programming. — Rob Pike Relational fields are used to represent model relationships. They can be applied to ForeignKey, ManyToManyField and OneToOneField relationships, as well as to reverse re

www.django-rest-framework.org

 

댓글