DjangoRestFrameworkで中間テーブルをネストした形のJsonで返す


なぜ書いたのか

Django歴が長くはなく、ましてやDRFは初めてで、うまく活用できず悩んだので書いていく。
日本語の記事も少ないと感じたので、少しでも参考になればいいと思う。

Django, djangorestframeworkの説明は省きます。

重要な部分はserializerです。

開発環境

macOS Mojave
python 3.6.7
Django 2.1.3
djangorestframework 3.9.0

Model

まずはDjangoのDocsにも書かれているmodelを例にとってモデルを定義していく

Models | Django documentation | Django

models.py

class Person(models.Model):
    name = models.CharField(max_length=128)
        play = models.CharField(max_length=128)

    def __str__(self):
        return self.name

class Group(models.Model):
    name = models.CharField(max_length=128)
    members = models.ManyToManyField(Person, through='Membership')

    def __str__(self):
        return self.name

class Membership(models.Model):
    person = models.ForeignKey(Person, on_delete=models.CASCADE)
    group = models.ForeignKey(Group, on_delete=models.CASCADE)
    date_joined = models.DateField()
    invite_reason = models.CharField(max_length=64)

Viewsets

最終的にはそのGroupのMenmberとMenbmerのname,invite_resonを表示したいと考える
viewsetsもここでは重要ではないので最小限に抑える

group_viewsets.py
class GroupViewsets(viewsets.ModelViewSet):
    queryset = Group.objects.all()
    serializer_class = GroupSerializer

Serialezer

  • 第一段階 MemberのIDがネストされた形のjsonで表示
serializer.py
class GroupSerializer(serializers.ModelSerializer):
    class Meta:
        model = Group
        fields = (
            'name',
            'members',
        )
{
  "name": "The Beatles",
  "members": [
    1,
    2,
    ...
  ]
}

memberに対してnameとplayも紐付いていて欲しいところ

  • 第二段階

membersに対してPersonの詳細もネストされた状態で紐付いている

serializer.py

class PersonSerializer(serializers.ModelSerializer):
    class Meta:
        model = Person
        fields = (
            'name',
            'play',
        )

class GroupSerializer(serializers.ModelSerializer):
    members = PersonSerializer(many=True)

    class Meta:
        model = Group
        fields = (
            'name',
            'members',
        )
{
  "name": "The Beatles",
  "members": [
    { 
      "name": "Ringo Starr",
      "play": "Drums"
    },
    { 
      "name": "Paul McCartney",
      "play": "Bass"
    },
    ...
  ]
}

Person情報が紐づいたはいいものの、invite_reasonも紐づいて欲しい

  • 第三形態(最終)

membersにinvite_reasonが紐づいたjsonが欲しい

serializers.py
class MembershipSerializer(serializers.ModelSerializer):
    name = serializers.ReadOnlyField(source="person.name")
    play = serializers.ReadOnlyField(source="person.play")

    class Meta:
        model = Membership
        fields = (
            'name',
            'play',
            'invite_reason',
        )

class GroupSerializer(serializers.ModelSerializer):
    members = MembershipSerializer(source='membership_set', many=True)

    class Meta:
        model = Group
        fields = (
            'name',
            'members',
        )

最初はMemberhipSerializerからpersonにアクセスしてnameをとるという発想ができなかった、僕はここでつまづきました.djangoの特性などがわかっていればすぐにできそうですが、、、

MembershipSerializerのperson=Personserializer()などやって、全然できずに悩んでいました.

{
  "name": "The Beatles",
  "members": [
    { 
      "name": "Ringo Starr",
      "play": "Drums",
      "invite_reason": "Needed a new drummer."
    },
    { 
      "name": "Paul McCartney",
      "play": "Bass"
      "invite_reason": "Wanted to form a band."
    },
    ...
  ]
}

これで思い通りの結果を取得できるようになりました。

今回学んだポイント

serializersのReadOnlyFieldやCharFieldで定義されていた関数、自分で定義した関数を呼び出すことが可能