Django REST framework로 중첩 된 리소스의 list 메소드

Django REST framework를 사용하는 API에서 "중첩 된 리소스 목록을 두 배로 돌려주세요"라고 말했기 때문에 "REST API의 표준 설계는군요 ..."라고 반대했지만, 한 적이 없기 때문에 만들어 보았다 때의 메모.

환경



파이썬 3.8.2
장고 3.0.5
djangorestframework 3.11.0

모델



Parent
├ Child1
└ Child2
↑이런 관계의 모델의 경우

models.py
from django.db import models

class Parent(models.Model):
    """
    親のモデル
    """
    parent_column = models.CharField(
        max_length=10, verbose_name='親モデルのカラム')

    def __self__(self):
        return self.parent_column

class Child1(models.Model):
    """
    子供のモデル1
    """
    # related_nameとserializerのfield名を合わせる
    parent = models.ForeignKey(Parent, on_delete=models.CASCADE, related_name='child1s')
    child1_column = models.CharField(
        max_length=10, verbose_name='子モデル1のカラム')

    def __self__(self):
        return self.Child1_column

class Child2(models.Model):
    """
    子供のモデル2
    """
    # related_nameとserializerのfield名を合わせる
    parent = models.ForeignKey(Parent, on_delete=models.CASCADE, related_name='child2s')
    child2_column = models.CharField(
        max_length=10, verbose_name='子モデル2のカラム')

    def __self__(self):
        return self.Child2_column

시리얼라이저



모델과 동일한 구성으로

models.py
from rest_framework import serializers
from .models import Parent, Child1, Child2

class Child1Serializer(serializers.ModelSerializer):
    """
    子供のserializer1
    """
    class Meta:
        model = Child1
        fields = [
            'id',
            'child1_column',
        ]

class Child2Serializer(serializers.ModelSerializer):
    """
    子供のserializer2
    """
    class Meta:
        model = Child2
        fields = [
            'id',
            'child2_column',
        ]

class ParentSerializer(serializers.ModelSerializer):
    """
    親のserializer
    """
    # 子供のserializerを親のfieldとして定義
    # このfield名とmodelのrelated_nameを合わせる
    child1s = Child1Serializer(many=True, read_only=True)
    child2s = Child2Serializer(many=True, read_only=True)

    class Meta:
        model = Parent
        fields = [
            'id',
            'parent_column',
            'child1s',
            'child2s',
        ]

보기



QuerySet는 나중에 괴롭히기 쉽도록, get_queryset()로 정의해 두는 것이 좋을 것 같다.

views.py
from rest_framework import generics
from rest_framework.pagination import PageNumberPagination

from .models import Parent
from .serializer import ParentSerializer

class NestedListView(generics.ListAPIView):
    """
    ネストしたリソースのlistメソッド
    listだけ使いたいのでgenerics.ListAPIViewを使用
    """
    pagination_class = PageNumberPagination
    serializer_class = ParentSerializer
    # クエリ―パラメータをfilterの検索条件に変換するdict
    CONDITION_KEYS = {
        'parent_column': 'parent_column__contains',
        'child1_column': 'child1s__child1_column__contains',
        'child2_column': 'child2s__child2_column__contains',
        }

    def get_queryset(self):
        """
        後々、複雑なQuerySetを書くことになるかもしれないので、
        ゴニョゴニョできる場所で定義しておく。
        """
        # 検索条件のdict
        condition_dict = {}
        for key, val in self.request.query_params.items():
            # query_paramsのkeyに検索条件の項目があったらfilter用のkeyに変換する
            if key in self.CONDITION_KEYS:
                condition_dict[self.CONDITION_KEYS[key]] = val

        queryset = Parent.objects.filter(**condition_dict).order_by('id').prefetch_related().distinct()

        return queryset

    def list(self, request, *args, **kwargs):
        """
        listメソッドをオーバライド
        """
        # querysetにpaginationを適用
        page_qs = self.paginate_queryset(self.get_queryset())
        # serializeを取得
        serializer = self.get_serializer(page_qs, many=True)
        # paginationつきで返却
        return self.get_paginated_response(serializer.data)


API 실행 결과



이런 느낌


간단했습니다.

출처



참고문헌


  • DRF 공식 문서

  • 그 밖에도 본 것 같지만 잊었다…

    좋은 웹페이지 즐겨찾기