Django Rest Frameworkの応答に(モデルを介して)仲介者を含める


110

m2m / throughモデルの扱いと、django restフレームワークでのそれらのプレゼンテーションについて質問があります。古典的な例を見てみましょう:

models.py:

from django.db import models

class Member(models.Model):
    name = models.CharField(max_length = 20)
    groups = models.ManyToManyField('Group', through = 'Membership')

class Group(models.Model):
    name = models.CharField(max_length = 20)

class Membership(models.Model):
    member = models.ForeignKey('Member')
    group = models.ForeignKey('Group')
    join_date = models.DateTimeField()

serializers.py:

imports...

class MemberSerializer(ModelSerializer):
    class Meta:
        model = Member

class GroupSerializer(ModelSerializer):
    class Meta:
        model = Group

views.py:

imports...

class MemberViewSet(ModelViewSet):
    queryset = Member.objects.all()
    serializer_class = MemberSerializer

class GroupViewSet(ModelViewSet):
    queryset = Group.objects.all()
    serializer_class = GroupSerializer

メンバーのインスタンスを取得すると、メンバーのすべてのフィールドとそのグループも正常に受信します。ただし、メンバーシップモデルからの追加の詳細なしで、グループの詳細のみを取得します。

言い換えれば、私受け取ることを期待します:

{
   'id' : 2,
   'name' : 'some member',
   'groups' : [
      {
         'id' : 55,
         'name' : 'group 1'
         'join_date' : 34151564
      },
      {
         'id' : 56,
         'name' : 'group 2'
         'join_date' : 11200299
      }
   ]
}

join_dateに注意してください

もちろん、Django Rest-Frameworkの公式ページなど、非常に多くの解決策を試しましたが、誰もそれについて適切な明確な答えを出していないようです-これらの追加フィールドを含めるには何をする必要がありますか?私はそれがdjango-tastypieでもっと簡単であるとわかりましたが、他のいくつかの問題があり、レストフレームワークを好みます。



8
これはおいしいパイ用です。私はDjango Rest Frameworkを使用しています。
mllm 2013年

回答:


139

どうだ…

MemberSerializerで、次のようにフィールドを定義します。

groups = MembershipSerializer(source='membership_set', many=True)

メンバーシップシリアライザーでこれを作成できます。

class MembershipSerializer(serializers.HyperlinkedModelSerializer):

    id = serializers.Field(source='group.id')
    name = serializers.Field(source='group.name')

    class Meta:
        model = Membership

        fields = ('id', 'name', 'join_date', )

これには、必要なメンバーシップをソースとするシリアル化された値であるグループを作成するという全体的な効果があります。次に、カスタムシリアライザーを使用して、表示するビットを引き出します。

編集:@bryanphによってコメントさserializers.fieldれたようにserializers.ReadOnlyField、DRF 3.0で名前が変更されたので、これは次のように読む必要があります。

class MembershipSerializer(serializers.HyperlinkedModelSerializer):

    id = serializers.ReadOnlyField(source='group.id')
    name = serializers.ReadOnlyField(source='group.name')

    class Meta:
        model = Membership

        fields = ('id', 'name', 'join_date', )

最新の実装の場合


2
fyi、私はこれの多くの変種を試しました、そしてこれを機能させることができません。これは公式ドキュメントにはありませんか?Membership_setはどこに定義されていますか?
2015年

3
membership_setメンバーのデフォルトの関連名です->メンバーシップ
dustinfarris

私にとっての秘訣は、「membership_set」の名前を見つけることでした。私には明示的な「関連」名のないスルーモデルがあったため、Django Many to Manyのドキュメントを読んで、その名前を推測する必要がありました。
ミセノ2016年

ヒントありがとうございます。ただし、この場合のDRFは直観に反していると思います。これは、クラスMemberが既にgroupsというm2mフィールドを定義しているため、このソリューションでは、シリアライザのフィールドを、モデルを介して逆の関係をポイントするように強制することでオーバーライドするようです。私はDRF実装の詳細にはあまり詳しくありませんが、おそらくモデルのイントロスペクションを使用すると、自動的に渡すことができます。考えてみてください:)
gru

これがDRFの最新バージョンで動作するかどうかについて更新していただけますか?または、少なくとも使用していたバージョンを教えてください。DRFにフィールドモデル全体を返すようにすることはできません-常に元の関係になります(メンバーシップではなく、常にグループを返します)。
Andrey Cizov 2016年

18

私はこの問題に直面しており、私の解決策(DRF 3.6を使用)は、オブジェクトでSerializerMethodFieldを使用し、次のようにMembershipテーブルを明示的にクエリすることでした。

class MembershipSerializer(serializers.ModelSerializer):
    """Used as a nested serializer by MemberSerializer"""
    class Meta:
        model = Membership
        fields = ('id','group','join_date')

class MemberSerializer(serializers.ModelSerializer):
    groups = serializers.SerializerMethodField()

    class Meta:
        model = Member
        fields = ('id','name','groups')

    def get_groups(self, obj):
        "obj is a Member instance. Returns list of dicts"""
        qset = Membership.objects.filter(member=obj)
        return [MembershipSerializer(m).data for m in qset]

これは、各dictがMembershipSerializerからシリアル化されているグループキーのdictのリストを返します。書き込み可能にするには、MemberSerializer内に独自のcreate / updateメソッドを定義して、入力データを反復処理し、Membershipモデルインスタンスを明示的に作成または更新できます。


-4

注:ソフトウェアエンジニアとして、私はアーキテクチャを使用するのが大好きで、開発のためのレイヤードアプローチに深く取り組んできたので、ティアを尊重してそれに答えます。

私が問題を理解したように、これがソリューションmodels.pyです

class Member(models.Model):
    member_id = models.AutoField(primary_key=True)
    member_name = models.CharField(max_length = 

class Group(models.Model):
    group_id = models.AutoField(primary_key=True)
    group_name = models.CharField(max_length = 20)
    fk_member_id = models.ForeignKey('Member', models.DO_NOTHING, 
                             db_column='fk_member_id', blank=True, null=True)

class Membership(models.Model):
    membershipid = models.AutoField(primary_key=True)
    fk_group_id = models.ForeignKey('Group', models.DO_NOTHING, 
                             db_column='fk_member_id', blank=True, null=True)
    join_date = models.DateTimeField()

serializers.py

import serializer

class AllSerializer(serializer.Serializer):
    group_id = serializer.IntegerField()
    group_name = serializer.CharField(max_length = 20)
    join_date = serializer.DateTimeField()

CustomModels.py

imports...

    class AllDataModel():
        group_id = ""
        group_name = ""
        join_date = ""

BusinessLogic.py

imports ....
class getdata(memberid):
    alldataDict = {}
    dto = []
    Member = models.Members.objects.get(member_id=memberid) #or use filter for Name
    alldataDict["MemberId"] = Member.member_id
    alldataDict["MemberName"] = Member.member_name
    Groups = models.Group.objects.filter(fk_member_id=Member)
    for item in Groups:
        Custommodel = CustomModels.AllDataModel()
        Custommodel.group_id = item.group_id
        Custommodel.group_name = item.group_name
        Membership = models.Membership.objects.get(fk_group_id=item.group_id)
        Custommodel.join_date = Membership.join_date
        dto.append(Custommodel)
    serializer = AllSerializer(dto,many=True)
    alldataDict.update(serializer.data)
    return alldataDict

技術的には、DataAccessLayerにリクエストを渡して、データアクセスレイヤーからフィルター処理されたオブジェクトを返す必要がありますが、私は質問にすばやく答える必要があるため、ビジネスロジックレイヤーのコードを調整しました!


1
これは完全にカスタマイズされたアプローチです。DjangoRest Frameworkは非常に柔軟ですが、Boundsでの作業のファンではないので、ほとんどのRest API開発に使用しています。
Syed Faizan

2
これは過度に設計されており、DRFも使用していません。
michauwilliam
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.