Django REST Framework メモ ::ForeignKeyでつながっている別モデルをくっつけてひとつのJSONとしてWebAPI出力する


はじめに

Django REST Framework でForeignKeyでつながっている別モデルをくっつけてひとつのJSONとしてWebAPI出力する実装方法です。

転載元ブログ情報

この記事は自分のブログの転載です。
http://k-mawa.hateblo.jp/entry/2018/02/08/000011

検証した環境

  • Django2
  • Python3.5.2

モデルの設定例

(例として記事モデル"Article"に対するコメント"Comment"が一覧がくっついてくるような出力を実装するとします)

  • 親モデル(アプリ名articleとします)
#article.models.py

class Article(models.Model):
    title = models.CharField(max_length=300, blank=True, null=True)
    cotents_text = models.TextField(blank=True, null=True)
    pubdate = models.DateTimeField(auto_now_add=True)
  • 子モデル(親モデルをForeignKeyにしている。アプリ名commentとします)
#comment.models.py

class Comment(models.Model):
    target_article = models.ForeignKey(Article, on_delete=models.SET_NULL)
    comment = models.TextField(blank=True, null=True)
    pubdate = models.DateTimeField(auto_now_add=True)

Serializerの設定例

comment.models.pyのCommentモデルのserializerをつくっておきます。

#comment.api.serializers.py

class CommentChildSerializer(ModelSerializer):
    class Meta:
        model = Comment
        fields = [
            'id',
            'target_article',
            'comment',
            'pubdate',
        ]

ここからが山場です

article.models.pyのArticleモデルのserializerをつくっておきます。

#article.api.serializers.py


from rest_framework.serializers import SerializerMethodField

from article.models import *
from comment.models import *
from comment.api.serializers import * 
#↑のインポートを忘れずに^^

class ArticleDetailSerializer(ModelSerializer):
    comments = SerializerMethodField() #このフィールドを加えると下記のように出力する値を操作できます。
    class Meta:
        model = Article
        fields = [
            'id',
            'title',
            'cotents_text',
            'pubdate',
            'comments', #モデルには存在しない追加する新フィールド
        ]

    def get_comments(self, obj):
        try:
            comment_abstruct_contents = CommentChildSerializer(Comment.objects.all().filter(target_article = Article.objects.get(id=obj.id)), many=True).data
            #↑ここを"Comment.objects.all().filter(target_article = Article.objects.get(id=obj.id)"
            #とだけにすると、"Item is not JSON serializable"というエラーが出ますので
            #Serializer(出力させたいもの).data という処理が必要です。
            return comment_abstruct_contents
        except:
            comment_abstruct_contents = None
            return comment_abstruct_contents

つぎにviewsを通常のRetrieveAPIViewとかけばOKです。

#article.api.views.py

from rest_framework.generics import RetrieveAPIView

from article.models import *
from comment.models import *
from comment.api.serializers import * 


class ArticleDetailAPIView(RetrieveAPIView):
    queryset = Article.objects.all()
    serializer_class = ArticleDetailSerializer
    lookup_field = 'pk'

最後にurls.pyを書いて動作確認しましょう。

from django.urls import path
from .views import ArticleDetailAPIView

urlpatterns = [
   path('detail_articles/<int:pk>/', ArticleDetailAPIView.as_view(), name='detail')
]

※引数とpath設定がDjango1系とは異なります

拡張モジュールも見つけました

DRFのJSON出力を拡張するのにこのモジュールも使えそうですので共有します。(上記の方法ならこのモジュールを使わずとも出力できますが、モジュールの需要もありそうなので共有します^^ )