在Django Rest Framework的响应中包括中介(通过模型)


110

我有一个关于通过模型处理m2m /及其在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

在获取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更加简单明了,但是还有其他一些问题,并且更喜欢休息框架。



8
这是美味的馅饼,我正在使用Django Rest Framework。
mllm 2013年

Answers:


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评论,在DRF 3.0中serializers.field被重命名为serializers.ReadOnlyField,因此应显示为:

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
费伊,我已经尝试了许多这种方法,但无法正常工作。这不是官方文档吗?成员集在哪里定义?
粘土人

3
membership_set是“成员”->“成员资格”的默认相关名称
灰尘farris

对我而言,技巧部分是发现“ membership_set”名称。我有一个贯通模型,没有明确的“相关”名称,所以我不得不通过阅读Django Many to Many的文档来猜测它的名称。
miceno '16

效果很好,谢谢提示。我认为,在这种情况下,DRF有点违反直觉,因为类Member已经定义了一个称为groups的m2m字段,并且该解决方案似乎通过强制其指向直通模型的反向关系来覆盖序列化程序中的字段。我不是很了解DRF的实现细节,但是通过模型自省,它可能会自动处理。只是一些
值得

在任何情况下,您都可以向我们更新此功能是否适用于最新版本的DRF?或者至少告诉您使用的是哪个版本?我无法使DRF返回直通字段模型-它总是以原始关系结束(而不是成为成员关系-它总是会返回Group)。
安德烈·齐佐夫

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]

这将返回组键的字典列表,其中每个字典都从MembershipSerializer序列化。为了使其可写,您可以在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
这是一种完全自定义的方法,我在我的大多数Rest API开发中都使用了该方法,因为即使Django Rest Framework非常灵活,我也不是Bounds的忠实拥护者!
Syed Faizan

2
这太过精心设计了,甚至不使用DRF。
michauwilliam
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.