DRF 공부하기 (6) :: ModelSerializer

PHYYOU·2020년 10월 18일
1
post-thumbnail

출처


위의 블로그의 내용을 복붙함.

DRF 6 - ModelSerializer

이전 사례에서 Serializer를 만들 때, 각 필드를 하나하나 정의해 주었다. 마치 모델을 다시 한 번 작성하는 것 같은 불편함이 있었다. 이 문제를 해결해 주는 것이 ModelSerializer이다.

ModelSerializer는 크게 아래와 같은 3가지 기능을 제공한다. 주는 편리함이 워낙 크기에 Base Serializer보다 훨씬 생산성을 높일 수 있다.

  • (의존하고 있는 모델에 기반해서) Serializer 필드를 자동으로 만들어 줌
  • Serializer를 위한 validator 제공 : ex) unique_together_validators
  • .create(), .update() 함수 기본으로 제공하여 다시 만들 필요 없음

1. ModelSerializer 사용 방법

본 사례에서는 아래와 같은 순서로 코드를 작성하였다.

1) class Meta 작성

  • model = 모델명
  • fields = __all__, exclude, 직접 명시 ('id', 'name')
  • read_only_field = ['id']

2) serializer로 정의해 줘야 되는 필드

  • 추가하고 싶은 필드가 있을 경우, serializer.SerializerMethodField()로 정의해 줌
  • ForeginKey로 연결된 필드가 있을 경우,
    • 디폴트는 ForeignKey로 연결된 필드의 pk를 가져옴
    • 만약 해당 데이터의 String이나 Hyperlink 를 가져오고 싶다면 별도로 지정을 해 줘야 함
class JournalistSerializer(serializers.ModelSerializer):

    class Meta:
        model = Journalist
        fields = '__all__'

class ArticleSerializer(serializers.ModelSerializer):

    time_since_publication = serializers.SerializerMethodField()

    class Meta:
        model = Article
        fields = "__all__"     # Article 모델의 모든 필드 시리얼라이즈
    
    def get_time_since_publication(self, object):
        publication_date = object.publication_date
        now = datetime.now(timezone.utc)   # 에러 해결. timezone 임포트한 뒤 추가
        time_delta = timesince(publication_date, now)
        return time_delta

datetime.now(timezone.utc)을 timzezone.now()로 바꾼 이유
https://it-eldorado.tistory.com/13 -> Aware 객체를 이용하기 위해서

2. Nested Relationships

앞써 언급했듯이 ForeignKey 필드를 사용하고자 할 때, 기본적으로 아무런 설정이 없으면 참조하고 있는 pk 값을 가져온다. 만약 pk 값 외에 다른 값을 가져오고 싶다면, 아래와 같은 메소드를 이용할 수 있다.

  • ForgeignKey로 연결된 모델의 __str__ 메소드에서 정의한 string를 리턴
  • 인자로 many=True, read_only=True 등을 가질 수 있음
class ArticleSerializer(serializers.ModelSerializer):
    ...
    author = serializers.StringRelatedField() 


[API 호출 결과]

[내 API 호출 결과]

Journalist 모델 def __str__(self): 에서 first_name과 last_name을 결합해 준 string이다.

[models.py]

class Journalist(models.Model):
    ...
    def __str__(self):
        return f"{ self.first_name } { self.last_name }"

  • 참조할 모델의 Serializer를 가져와서 사용 (참조할 Serializer가 더 위에 위치해야 참조할 수 있음)
  • 인자로 many=True, read_only=True 등을 가질 수 있음
  • Journalist <- Article이 정참조하는 관계 (1 : N)로서 ArticleSerializer에서 author를 참조할 수 있고, 반대로 JournalistSerializer에서 articles를 참조할 수 도 있음
class JournalistSerializer(serializers.ModelSerializer):
    ...
    # articles = ArticleSerializer(many=True, read_only=True)

class ArticleSerializer(serializers.ModelSerializer):
    ...
    author = JounalistSerializer(read_only=True)


(

[API 호출 결과]

[내 API 호출 결과]

이 필드는 Read only로만 사용된다. 비슷한 역할을 하는 모듈로 PrimaryKeyRelatedField가 있는데, 이 모듈은 타겟 모델의 primary key 값을 가져오게 된다.

  • HyperlinkedRelatedField는 ForgeignKey로 연결된 타겟 필드의 API url을 리턴
  • many, read_only, view_name 등 지정해 줘야 함.
    • 특히, view_name은 참조할 api의 url의 name을 명시해야 함

[코드]

[serilaizers.py]
class JournalistSerializer(serializers.ModelSerializer):
  ...
  articles = serializers.HyperlinkedRelatedField(many=True,
             read_only=True, view_name="article-detail")


[views.py]
class JournalistListCreateAPIView(APIView):
    
  def get(self, request):
    journalists = Journalist.objects.all()
    serializer = JournalistSerializer(journalists, many=True,
                 context={'request': request})
    return Response(serializer.data)



[API 호출 결과]

[내 API 호출 결과]

[참고] Including extra context / 관련 tutorial

Serializer에 queryset 혹은 instance 외에 추가로 정보를 전달하고 싶을 때에는 context를 이용해서 전달할 수 있다. 앞의 사례에서는 request란 key값으로 request object를 전달한 경우이다.

이를 Debug하여 자세히 어떤 데이터들이 담겨있는지 살펴보면 아래 그림과 같다.

profile
박효영

0개의 댓글