[Django] SerializerMethodField로 모델 필드 값을 변형해서 새로운 필드로 반환하기

oen·2020년 12월 17일
2

🍡 Django

목록 보기
1/4
post-thumbnail

만약 시리얼라이저로 객체를 직렬화한 JSON에

  • 모델에 없는 필드를 추가하고 싶거나

  • 모델에 있는 값을 변형해서 새로운 필드의 값으로 넣고 싶으면 (예를 들어 모델에는 full_name 필드만 있는데 여기서 이름만 뽑아내서 JSON에 first_name을 추가)

SerializerMethodField 를 사용하면 된다.

Example

객체의 모델에는 full_name 필드만 있다고 하자.

BEFORE

"full_name": '{"first_name": "en", "last_name": "o"}',

JSON에는 full_name 필드에 해당하는 객체의 "full_name" 속성만 포함된다.

기본적으로 객체를 시리얼라이저를 통해 JSON으로 직렬화할 때
model 에 정의된 필드는 Serializer 클래스 안의 Meta 클래스(

class Meta:
    model = MODEL클래스이름
    ...

)에서 model = MODEL클래스이름 만 추가하면 자동으로 시리얼라이저 필드로 추가되지만

모델에 last_name 필드가 없어서 시리얼라이저 필드에 추가할 수가 없기 때문이다.

AFTER

"id": 5238,
"full_name": '{"first_name": "o", "last_name": "en"}',
"last_name": "en"

하지만 SerializerMethodField 를 사용하면 객체의 모델에 last_name 필드가 없음에도 불구하고 JSON에 "last_name" 속성도 포함할 수 있다.

그리고 "last_name" 속성에는 객체의 full_name 속성으로부터 "last_name" 키 값인 "en" 만 뽑아 넣을 수도 있다.


How?

2가지만 하면 된다.

  1. serializer에 필드 추가
    - SerializerMethodField로 (모델에 없는) 새로운 필드를 추가한다.
    -> JSON에 필드가 추가됨
  2. serializer에 method 추가
    -> 객체의 속성 값을 변형해 새로운 필드의 값에 넣을 수 있다.
class VideoSerializer(serializers.Serializer):
    full_name = JSONField
	last_name = serializers.SerializerMethodField() # 1. 필드 추가

    class Meta:
        model = Video

    def get_first_name(self, obj): # 2. 메소드 추가. 이 메소드는 객체를 인자로 받고
        return obj.full_name['last_name'] # 객체의 full_name 속성 값에서 'last_name' 키 값을 리턴

이 2가지 방법에 대한 자세한 설명은 아래의 공식문서에 나와있다.

SerializerMethodField 공식문서

본문

This is a read-only field. It gets its value by calling a method on the serializer class it is attached to. It can be used to add any sort of data to the serialized representation of your object.

Signature: SerializerMethodField(method_name=None)

  • methodname - The name of the method on the serializer to be called. If not included this defaults to get<field_name>.
    The serializer method referred to by the method_name argument should accept a single argument (in addition to self), which is the object being serialized. It should return whatever you want to be included in the serialized representation of the object. For example:
from django.contrib.auth.models import User
from django.utils.timezone import now
from rest_framework import serializers

class UserSerializer(serializers.ModelSerializer):
    days_since_joined = serializers.SerializerMethodField()

    class Meta:
        model = User

    def get_days_since_joined(self, obj):
        return (now() - obj.date_joined).days

번역

SerializerMethodField는 read-only 필드이다.
(모델 필드가 아니기 때문에 SerializerMethodField로 정의한 필드에는 값을 저장할 수도 수정할 수도 없다.)
SerializerMethodField가(days_since_joined) 있는 serializer class의(UserSerializer) 메소드를(def get_days_since_joined(self, obj)) 호출함으로서 필드에 어떤 값을 가져올 수 있다.

Signature SerializerMethodField(method_name=None)

  • method_name - 호출될 serializer 메소드의 이름을 넣는다.
    (method_name=get_name)
    만약 method_name 인자에 메소드 이름을 넘기지 않는다면 기본으로 get<field_name> 가 된다.

method_name argument에 의해 참조되는 serializer의 메소드는 (self 외에도) 하나의 argument를 받는다.
그 argument(obj)는 바로 직렬화(serialized)되는 객체이다.
(class Meta에 지정한 모델의 객체이다.)
serializer 메소드는 객체를 직렬화한 결과에 포함되길 원하는 것((now() - obj.date_joined).days)을 리턴하도록 하게 하면 된다. 예시:

from django.contrib.auth.models import User
from django.utils.timezone import now
from rest_framework import serializers

class UserSerializer(serializers.ModelSerializer):
    days_since_joined = serializers.SerializerMethodField()

    class Meta:
        model = User

    def get_days_since_joined(self, obj): # 객체를 arguemnt로 받는다
        return (now() - obj.date_joined).days # 이 메소드는 받은 객체를 직렬화해 원하는 형태로 변환해서 반환한다

정리

여기까지 공식문서 내용이다. 공식문서 예시를 보면서 하나씩 정리해보자.

  1. 우리는 User 모델의 객체를 UserSerialzier를 통해 json 형식으로 직렬화한 결과를 전송해야 한다. (※ json이 json형식으로 직렬화 하고나서 다시 역직렬화하기도 좋고, 데이터 전송하기도 좋은 공통적인 규격이어서 json을 많이 쓴다)

  2. User 모델에 가입 날짜를 저장한 date_joined 필드는 있지만, 지금까지 가입한지 며칠이 되었는지를 저장하는 days_since_joined 필드는 없다.

  3. 하지만 객체의 date_joined 필드 값 만으로 days_since_joined 값을 계산할 수 있다는 걸 알고있다.
    (now() - obj.date_joined).daysnow()에서 obj.date_joined(가입날짜)를 빼면 된다.

  4. days_since_joined = serializers.SerializerMethodField()
    로 새롭게 추가하고 싶은 필드를 시리얼라이저에 추가한다.

  5. def get_days_since_joined
    시리얼라이저 메소드를 정의한다.
    새로 추가한 필드의 method_name 인자에는 이 메소드의 이름을 넣는다. 이 이름은 필드의 이름(days_since_joined)에 따른 영향을 받지 않기 때문에 아무렇게나 지어도 된다.
    또한 serializers.SerializerMethodField()처럼 method_name 인자를 넣지 않아도 method_name 인자의 값이 디폴트로 get<field_name> 로 들어간다. 따라서 꼭 method_name 인자를 넣지 않아도 된다.
    대신 method_name 인자를 넣지 않는다면 함수 이름은 꼭 get<field_name>으로 적어야 한다.
    이 메소드는 객체를 인자로 받는다. 메소드의 내용은 객체로부터 변형한 값을 리턴하게 하면 된다. 이 메소드로부터 리턴된 값은 새롭게 추가한 필드의 값으로 들어간다.

  6. 객체를 직렬화한 JSON에 새로운 필드와 값이 추가된다.


참고

serialization (직렬화)
객체 자체는 메모리상에 존재한다. 이를 저장할 수 있는가?
이는 직렬화 과정을 통해 저장이 가능하다. 예를 들면 어떤 클래스 자체의 인스턴스를 데이터베이스에 저장하는 것이 아니라, 메모리상에서 바로 저장할 수 있도록 하는 것이다.
데이터 구조나 오브젝트 상태를 동일하거나 다른 컴퓨터 환경에 파일이나 메모리 버퍼 상으로 저장, 또는 나중에 재구성할 수 있는 포맷으로 변환하는 과정이다.
파일의 데이터는 연속적인 데이터인 것에 반해, 메모리상의 데이터는 연속적인 데이터가 아니다.
이 메모리상의 데이터를 연속적으로(파일상의 데이터로) 만드는 것이 직렬화라고 한다. 그리고 반대로 메모리상의 객체는 역직렬화를 통해서 불러오게 된다.

serializer
serializer란, 어떤 모델 클래스가 있을 때, 이 클래스 인스턴스가 어떻게 json 형태로 바뀌는지(직렬화), 그리고 json 데이터는 어떻게 다시 클래스 인스턴스로 바뀌는지(역직렬화) 정의를 하는 것을 말한다.

profile
🐾

0개의 댓글