Включить посредника (через модель) в ответы в 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, где вы перебираете входные данные и явно создаете или обновляете экземпляры модели членства.