DRF 14 - DRF 공식문서 요약

Joey Lee·2020년 7월 15일
4

Django REST Framework

목록 보기
16/16

1. Serializer 관련

Saving serializer (Create & Update)

# .save() will create a new instance.
serializer = CommentSerializer(data=tata)

# .save() will update the existing `comment` instance.
serializer = CommentSerializer(comment, data=data)

# Passing additional attributes to .save() which is not part of the request data
serializer.save(owner=request.user)

Partial updates

# Update `comment` with partial data
serializer = CommentSerializer(comment, data={'content': 'foo bar'}, partial=True)

Dealing with nested objects

참조관계를 갖는 다른 모델의 데이터를 사용해야 할 경우, 아래와 같이 참조할 모델의 Serializer를 가져와서 사용할 수 있다.

  • 만약, nested representation이 반드시 필요한 경우가 아니라면, required=False를 설정하면 된다.
  • 만약, 리스트로 가져와야 된다면 many=True를 설정하면 된다.
class UserSerializer(serializers.Serializer):
    email = serializers.EmailField()
    username = serializers.CharField(max_length=100)

class CommentSerializer(serializers.Serializer):
    user = UserSerializer(required=False)
    edits = EditItemSerializer(many=True)  # A nested list of 'edit' items.
    content = serializers.CharField(max_length=200)
    created = serializers.DateTimeField()

Writable nested representations

만약 writable nested representation을 하고 싶다면, 여러 개의 객체를 저장할 수 있는 create() 메소드와 update() 메소드를 작성해야 한다.

1) .Create() 작성하기

아래 사례는 profile을 nested로 가지고 있는 user를 생성하는 코드이다.

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

[코드 해석]

  • 최초 validated_data에는 username, email, profile 정보가 담겨져 있다.
  • .pop('profile')메소드로 profile 정보만 추출하여 profile_data에 담고, validated_data에는 username, email이 남는다.
  • User 인스턴스와 해당 User의 Profile 인스턴스를 생성한다.
  • 마지막으로 user 인스턴스를 리턴해 준다.
2) .update() 작성하기

update를 위해서는 고려해야 되는 사항들이 많다.
많약 관계를 맺는 user의 profile이 존재하지 않으면 어떻게 할 것인가와 같은 문제가 생길 수 있다.

따라서 update를 작성할 때는 엄격하고 디테일하게 코드를 작성해야 한다.
디폴트 ModelSerializer .created(), .update() 메소드는 writable nested representations을 지원하지 않기 때문이다.

def update(self, instance, validated_data):
        profile_data = validated_data.pop('profile')
        # Unless the application properly enforces that this field is
        # always set, the follow could raise a `DoesNotExist`, which
        # would need to be handled.
        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

여러 개의 related instances를 저장하는 또 다른 방법은 custom model manager 클래스를 만들어서, 복수의 객체를 생성해 버리는 것이다.

가령, 유저 인스턴스를 생성할 때 프로파일 인스턴스도 항상 동시에 생성시키고자 한다면 아래와 같은 custom manager 클래스를 만들어서 사용하는 것도 한 방법이다.

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
    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']
    )

Dealing with multiple objects

many=True를 세팅하면, 복수의 객체들을 serialize할 수 있다.

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'}
# ]

Including extra context

2. Serialzier relations

1) StringRelated field

__str__ 메소드에서 리턴하는 값을 리턴해 줌.

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

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

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

2) PrimaryKeyRelated field

  • id를 리턴함
  • queryset을 명시해 주거나 혹은 read_only=True을 설정해 줘야 함. (안 그러면 에러)

[Arguments]

  • queryset - The queryset used for model instance lookups when validating the field input. Relationships must either set a queryset explicitly, or set read_only=True.
  • many - If applied to a to-many relationship, you should set this argument to True.
  • allow_null - If set to True, the field will accept values of None or the empty string for nullable relationships. Defaults to False.
  • pk_field - Set to a field to control serialization/deserialization of the primary key's value. For example, pk_field=UUIDField(format='hex') would serialize a UUID primary key into its compact hex representation.

3) Nested relationships

  • nested된 Track의 모든 데이터를 가져오려면,
  • TrackSerializer를 만든 뒤, tracks = TrackSerializer를 해줘야 한다.
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_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},
        ...
    ],
}

4) Writable nested serializers

  • Default nested serializers는 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>
profile
안녕하세요!

0개의 댓글