5
5

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

Django REST framework でネストしたリソースのlistメソッド

Posted at

Django REST framework を使ってるAPIで「ネストしたリソースのリストをドバっと返せ」と言われたので「REST APIの標準的な設計はですね…」と反対しかけたけど、やったことないので作ってみた際のメモ。

環境

Python 3.8.2
Django 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実行結果

こんな感じ
NestedListAPI.png

簡単でした。

ソース

参考文献

他にも見た気がするけど忘れた…

5
5
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
5
5

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?