[DRF] Serializer 정의하는 방법 - Nested Serializer vs ListField()

Jinhyung Rhee·2022년 8월 13일
0

/api/catetag/를 그대로 DRF를 이용하여 구현하기(/api2/catetag/)

  • 실제 응답값 확인(chrome 개발자도구)
{"cateList": ["IT", "Django", "\uc77c\uc0c1\uc0dd\ud65c"], "tagList": ["django", "technical board", "\ud30c\ub9ac", "\uac1c\uc120\ubb38", "\ub178\ud2b8\ubd81", "\uc5ec\ud589", "\uadf8\ub79c\ub4dc\uce90\ub2c8\uc5b8", "\uc6b4\ub3d9"]}
  • TODO : CateTagSerializer를 meta class를 이용하여 정의하는 것이 아니라, 필드를 직접 정의하여 코딩

필드와 출력 포맷 맞추는 방식

  • 출력 포맷 예시

    {
      name : "kim",
      age : 25
    }
  • 그에 대응하는 serializer 형식 예시

    ASerializer():
    	name = CharField()
     	age = IntegerField()
    
    • 출력 포맷의 key를 '필드명'으로 함(name, age)
    • 오른쪽에는 값(value)을 넣는 대신 '타입'을 지정함(CharField(), IntegerField())
    • Q. 값은 어떻게 집어넣을까?
      • A. 값은 serializer를 호출할 때, instance 인자에 넣은 값이 맵핑(mapping)됨!
      // KK
      {
        name : 'kim',
        age : 25
      }
      ASerializer(instance=kk)
  • ⭐CateTagSerializer의 경우⭐

    CateTagSerializer():
       cateList = 타입
       tagList = 타입
    • cateList의 타입은 List(=Array)임!

      • DRF에서 List 표현방식1 : ListField()

        • ❗ListField()는 Django에는 없고 DRF에만 존재하는 필드임❗
      • DRF에서 List 표현방식2 : Serializer(many=True)
        - many=True : 동일한 Type이 반복된다는 의미!
        - ex) 'IT', 'Django', '일상생활' -> Category 모델의 name 속성들이 반복되는 것!
        - ex) 'django', 'technical board', '파리', '개선문', '노트북', '여행', '그랜드캐니언', '운동' -> Tag 모델의 name 속성들이 반복되는 것!

         # (1)반복되는 속성들을 Serializer로 표현
         CategorySerializer():
            class Meta:
                model = Category
                fields = ['name']
        
         TagSerializer():
            class Meta:
                model = Tag
                fields = ['name']
        # (2)최종적으로 client에게 내려주는 값을 정의하는 Serializer
        # meta class를 이용하지 않고 필드를 직접 정의하는 방식
        # 즉, Serializer를 또다른 Serializer의 '타입'으로 지정하는 방식
        CateTagSerializer():
           cateList = CategorySerializer(many=True)
           tagList = TagSerializer(many=True)

DRF에서 List 표현방식2 : Serializer(many=True) 적용하기

  • urls.py

    from django.urls import path
    from . import views
    
    # url 패턴이 list나 detail이 아니면 name을 직관적으로 작명함
    urlpatterns = [
      path('post/', views.PostListAPIView.as_view(), name='post-list'),
      path('post/<int:pk>/', views.PostRetrieveAPIView.as_view(), name='post-detail'),
      path('comment/', views.CommentCreateAPIView.as_view(), name='comment-create'),
      path('post/<int:pk>/like/', views.PostLikeAPIView.as_view(), name='post_like'),
      path('catetag/', views.CateTagAPIView.as_view(), name='catetag'),
    ]
  • serializers.py

    # 반복되는 속성들을 표현하는 Serializer
    class CategorySerializer(serializers.ModelSerializer):
        class Meta:
            model = Category
            fields = ['name']
    
    class TagSerializer(serializers.ModelSerializer):
        class Meta:
            model = Tag
            fields = ['name']
    
    # 최종적으로 client에게 내려주는 값을 정의하는 Serializer
    # 직접 필드를 정의할 때는 Serializer 클래스를 상속받아서 사용!
    # 1. Nested Serializer 사용
    class CateTagSerializer(serializers.Serializer):
        cateList = CategorySerializer(many=True)
        tagList = TagSerializer(many=True)
    • class CateTagSerializer(serializers.Serializer): : 직접 필드를 정의할 때는 ModelSerializer가 아니라, Serializer 클래스를 상속받아서 사용!
  • views.py

    # Category와 Tag 두 테이블 모두를 가져와서 사용 -> APIView 또는 GenericAPIView 사용!
    # Generic View들은 모두 하나의 테이블을 대상으로 처리하는 View**
    class CateTagAPIView(APIView):
      # 직렬화(serialize) 방향: 테이블 -> serialize -> response
      def get(self, request, *args, **kwargs):
        # 1. 테이블
        cateList = Category.objects.all()
        tagList = Tag.objects.all()
        # 1-1. serializer에 넘겨줄 data 구성(중요: key와 필드명 일치!)**
        data = {
          'cateList' : cateList,
          'tagList' : tagList,
        }
        # 2. serialize
        serializer = CateTagSerializer(instance=data)
        # 3. response
        return Response(serializer.data)
    • CreateAPIView나 ListAPIView 같은 generic view들은 모두 하나의 테이블을 대상으로 처리하는 View임! -> 기존의 CRUD generic view가 안 맞다고 생각될 경우에는 APIViewGenericAPIView를 상속받아서 사용!
    • GenericAPIView : get_queryset이나 get_object와 같은 테이블 처리 메서드가 들어있음. 이러한 테이블 처리 메서드를 재활용할 예정이면 GenericAPIView를 사용!
      • ❗DRF에서 'Generic'의 의미 : DB와 관련된 처리를 하는 뷰❗
    • APIView : 재활용하는 로직이 거의 없이, 새로 코딩하는 로직이라면 APIView를 상속받음!
  • 결과

    • 비슷하지만... List 안에 객체들이 들어있는 형태임!
    • 객체 하나 하나가 CategorySerializer와 TagSerializer를 표현한 것임!
    • many=True 인자를 주었기 때문에, 여러 개의 객체(CategorySerializer/TagSerializer)들이 List 안에 한꺼번에 표현된 것
    • 우리가 해야할 것은 List(Array) 안에 '이름'만 나오도록 해야함!
  • 설명

    • Nested Serializer : serializer도 또 하나의 필드 Type이므로 serializer 속에 필드 Type으로 들어갈 수 있음
    • Nested Serializer에 many=True 옵션을 주면, item의 List를 표현할 수 있음!

DRF에서 List 표현방식1 : ListField() 사용하기⭐

  • urls.py

    from django.urls import path
    from . import views
    
    # url 패턴이 list나 detail이 아니면 name을 직관적으로 작명함
    urlpatterns = [
      path('post/', views.PostListAPIView.as_view(), name='post-list'),
      path('post/<int:pk>/', views.PostRetrieveAPIView.as_view(), name='post-detail'),
      path('comment/', views.CommentCreateAPIView.as_view(), name='comment-create'),
      path('post/<int:pk>/like/', views.PostLikeAPIView.as_view(), name='post_like'),
      path('catetag/', views.CateTagAPIView.as_view(), name='catetag'),
    ]
  • serializers.py : ListField() 사용⭐

    # 반복되는 속성들을 표현하는 Serializer
    class CategorySerializer(serializers.ModelSerializer):
        class Meta:
            model = Category
            fields = ['name']
    
    class TagSerializer(serializers.ModelSerializer):
        class Meta:
            model = Tag
            fields = ['name']
    
    # 최종적으로 client에게 내려주는 값을 정의하는 Serializer
    # 직접 필드를 정의할 때는 Serializer 클래스를 상속받아서 사용!
    # 2. ListField() 사용
    class CateTagSerializer(serializers.Serializer):
        cateList = serializers.ListField(child=serializers.CharField())
        tagList = serializers.ListField(child=serializers.CharField())
  • views.py

      # Category와 Tag 두 테이블 모두를 가져와서 사용 -> APIView 또는 GenericAPIView 사용!
      # Generic View들은 모두 하나의 테이블을 대상으로 처리하는 View**
      class CateTagAPIView(APIView):
        # 직렬화(serialize) 방향: 테이블 -> serialize -> response
        def get(self, request, *args, **kwargs):
          # 1. 테이블
          cateList = Category.objects.all()
          tagList = Tag.objects.all()
          # 1-1. serializer에 넘겨줄 data 구성(중요: key와 필드명 일치!)**
          data = {
            'cateList' : cateList,
            'tagList' : tagList,
          }
          # 2. serialize
          serializer = CateTagSerializer(instance=data)
          # 3. response
          return Response(serializer.data)
  • 결과

    • 원하는 결과가 출력됨!

Reference

https://www.inflearn.com/course/%EC%9E%A5%EA%B3%A0-drf/dashboard

profile
기록하는 습관

0개의 댓글