r잡초처럼
바른 프로그래밍
r잡초처럼
전체 방문자
오늘
어제
  • 분류 전체보기 (124)
    • FastAPI (7)
    • 끄적끄적 (2)
    • Python (17)
    • Django (31)
    • Database (2)
    • Docker (7)
    • 디자인패턴 (2)
    • CS 공부 (12)
      • 알고리즘 (2)
      • 자료 구조 (1)
      • 네트워크 (7)
      • IT 지식 (1)
      • 운영체제 (1)
    • 기타 팁들 (10)
    • Aws (2)
    • 독서 (1)
    • 코딩테스트 공부 (1)
      • 백준 (0)
      • 프로그래머스 (1)
    • DevOps (13)
    • TIL (3)

블로그 메뉴

  • 홈
  • 태그
  • 방명록

공지사항

인기 글

태그

  • pycharm
  • 모두의 네트워크
  • 책 리뷰
  • 상속 안티 패턴
  • encoding
  • 5장 회사에서 하는 랜 구성
  • poetry
  • depends
  • 7장
  • 물리 계층
  • pytest
  • validate
  • CS 지식
  • 컴퓨터 기본 지식
  • query param
  • 케이블의 종류
  • dotenv
  • Batch
  • 파이썬 클린 코드
  • 네트워크
  • 전기 신호
  • docker
  • cp949
  • 상속과 컴포지션
  • 랜 카드
  • 랜과 왠
  • 6장
  • preonboarding
  • 완벽한 IT 인프라 구축을 위한 Docker
  • fastapi

최근 댓글

최근 글

티스토리

hELLO · Designed By 정상우.
r잡초처럼

바른 프로그래밍

Django

[DRF] Serializer relations

2023. 2. 26. 20:06

Model 간의 관계를 Serializer를 통해 표현하려면 어떻게 해야 할까? 한 번 알아보자.

데이터 간의 관계는 OneToOne, ForeignKey, ManyToMany 등 다양하다. 이를 Serializer에서 표현하려면 어떻게 할까?

 

NOTE! 
DRF에서는 쿼리 최적화까진 해주지 않는다. 이는 프로그래머의 몫이므로 쿼리 최적화를 하자.

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)

StringRelatedField

관계의 대상을 __str__ 메서그를 통해 string으로 표현할 수 있다.

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

PrimaryKeyRelatedField

PK를 관계의 대상을 나타내는 데 사용할 수 있다.

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-write 이지만, read_only 플래그를 사용할 수 있다.

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

Nested relationships

사실 이거 때문에 이 포스팅을 쓰게 되었다. 중첩 관계를 표현할 때 유용한다. 필드가 to-many 관계를 나타내는 데 사용되는 경우 serializer 필드에 many=True 플래그를 추가해야 한다.

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

Writable

중첩 관계는 read-only이다. 따라서 쓰기 작업을 지원하려면 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>

Custom relational fields

필요한 표현에 맞는 관계형 스타일이 없는 경우 구현할 수 있다. RelatedField를 재정의하고 to_representation() 메서드를 구현해야 한다. 이 메서드는 필드의 대상을 값 인수로 사용하며 대상을 직렬화하는 데 사용해야 하는 표현을 반환해야 한다. 값 인수는 일반적으로 모형 인스턴스가 된다. 읽기-쓰기 관계형 필드를 구현하려면 .to_internal_value() 메서드도 구현해야 한다. 콘텍스트를 기반으로 동적 쿼리 집합을 제공하려면 클래스에서 .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.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)',
        ...
    ]
}

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

만약 이름을 설정하지 않은 경우 인수에서 자동으로 생성된 이름을 사용해야 한다.

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

'Django' 카테고리의 다른 글

Django Filterset 활용하기  (0) 2023.03.13
Django staticfile 관리하기  (0) 2023.03.01
[DRF] create vs perform_create  (0) 2023.02.24
[DRF] APIView vs ViewSet  (0) 2023.02.20
Django 구조 잡기  (0) 2023.02.18
    'Django' 카테고리의 다른 글
    • Django Filterset 활용하기
    • Django staticfile 관리하기
    • [DRF] create vs perform_create
    • [DRF] APIView vs ViewSet
    r잡초처럼
    r잡초처럼
    오늘보다 내일 더 나은 개발자가 되기 위한 노력을 기록하는 블로그 입니다.

    티스토리툴바