Включить посредника (через модель) в ответы в Django Rest Framework

У меня есть вопрос о работе с моделями 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

Когда GETing экземпляра Member, я успешно получаю все поля членов, а также его группы, но я получаю только данные групп, без дополнительных деталей, которые поступают из модели Membership.

Другими словами, я ожидаю:

{
   '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, но имел некоторые другие проблемы и предпочел отдых-рамки.

Ответ 1

Как насчет.....

На вашем 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', )

Это имеет общий эффект создания сериализованного значения, групп, у которого в качестве источника есть членство, которое вы хотите, а затем он использует собственный сериализатор для вытягивания бит, который вы хотите отобразить.

EDIT: как комментировал @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

Я столкнулся с этой проблемой, и мое решение (используя 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]

Это вернет список dicts для ключа группы, где каждый dict сериализуется из MembershipSerializer. Чтобы сделать его доступным для записи, вы можете определить свой собственный метод create/update внутри MemberSerializer, где вы перебираете входные данные и явно создаете или обновляете экземпляры модели членства.