【问题标题】:Include intermediary (through model) in responses in Django Rest Framework在 Django Rest Framework 的响应中包含中介(通过模型)
【发布时间】:2013-06-19 20:28:20
【问题描述】:

我有一个关于处理 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

在获取 Member 实例时,我成功接收到所有成员的字段及其组 - 但是我只获取组的详细信息,没有来自 Membership 模型的额外详细信息。

换句话说,我期望收到:

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

注意加入日期

我已经尝试了很多解决方案,当然包括Django Rest-Framework official page about it,但似乎没有人对此给出正确的简单答案 - 我需要做什么才能包含这些额外的字段?我发现使用 django-tastypie 更直接,但还有一些其他问题,并且更喜欢 rest-framework。

【问题讨论】:

标签: python django django-rest-framework


【解决方案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', )

这具有创建序列化值、组的总体效果,该值以您想要的成员身份作为其来源,然后它使用自定义序列化程序提取您想要显示的位。

编辑:正如@bryanph 评论的那样,serializers.field 在 DRF 3.0 中被重命名为 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', )

适用于任何现代实现

【讨论】:

  • 仅供参考,我已经尝试了很多变体,但无法正常工作。这不在官方文档中? members_set 在哪里定义?
  • membership_set 是 Member -> Membership 的默认相关名称
  • 对我来说,诀窍在于发现“membership_set”名称。我有一个没有明确的“相关”名称的直通模型,所以我不得不通过阅读Django Many to Many 的文档来猜测它的名称。
  • 这很好用,感谢您的提示。但我认为,在这种情况下,DRF 有点违反直觉,因为类 Member 已经定义了一个名为 groups 的 m2m 字段,并且该解决方案似乎通过强制它指向与直通模型的反向关系来覆盖序列化器中的字段。我不太了解 DRF 实现细节,但可能通过模型​​自省它可以自动处理。只是一些思考的食物:)
  • @AndreyCizov 我正在使用 DRF 3.5.3 并且上述解决方案有效。但是,我现在无法弄清楚如何使序列化程序“可更新”,例如通过 Group 序列化程序创建一个包含一堆成员的 Group。由于我们使用的是 ReadOnlyField(),因此数据甚至无法通过。
【解决方案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]

这将返回组键的字典列表,其中每个字典都从 MembershipSerializer 序列化。为了使其可写,您可以在 MemberSerializer 中定义自己的 create/update 方法,在其中迭代输入数据并显式创建或更新 Membership 模型实例。

【讨论】:

  • 我不明白如何使用更新。 SerializerMethodField 毕竟是只读的,那么如何从中访问验证数据呢?如果有人尝试,它会返回一个空字典。你能详细说明一下吗? @FariaC
【解决方案3】:

注意:作为一名软件工程师,我喜欢使用架构,并且我深入研究了分层开发方法,因此我将就层级来回答它。

据我了解,这是解决方案 模型.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()

序列化器.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,该请求将从数据访问层返回过滤后的对象,但由于我必须快速回答问题,所以我调整了业务逻辑层中的代码!

【讨论】:

  • 这是一种完全自定义的方法,我在大多数 Rest API 开发中都使用它,因为即使 Django Rest Framework 非常灵活,我也不是真正喜欢使用 Bounds!
  • 这太设计了,甚至没有使用 DRF。
猜你喜欢
  • 1970-01-01
  • 2021-08-07
  • 1970-01-01
  • 2015-06-27
  • 1970-01-01
  • 2013-04-09
  • 2022-06-14
  • 2015-12-10
  • 2019-01-16
相关资源
最近更新 更多