3

I am having difficulty serializing an intermediary "pivot" model and attach to each item in an Many-to-many relation in Django Rest Framework.

Example:

models.py:

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:

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

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

class MembershipSerializer(ModelSerializer):
    class Meta:
        model = Membership

I tried to follow the answers: Include intermediary (through model) in responses in Django Rest Framework

But it's not exactly what I need

I need to generate the following output

{
  "id": 1,
  "name": "Paul McCartney",
  "groups": [
    {
      "id": 3,
      "name": "Beatles",
      "membership": {
        "id": 2,
        "member_id": 1,
        "group_id": 3,
        "join_date": "2018-08-08T13:43:45-0300"
      }
    }
  ]
}

In this output I'm returning the related "Through Model" for each item in groups.

How can I generate serialize models in this way?

bdex
  • 43
  • 1
  • 6

2 Answers2

4

Based on the way you would like to show your output, I suggest you change your models to:

class Group(models.Model):
    name = models.CharField(max_length=20)
    members = models.ManyToManyField(
        'Membership',
        related_name='groups',
        related_query_name='groups',
    )


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


class Membership(models.Model):
    group = models.ForeignKey(
        'Group',
        related_name='membership',
        related_query_name='memberships',
    )
    join_date = models.DateTimeField()

How Group model and Member model are ManytoMany, does not have a problem you let the relationship in Group model. It will be the easiest to output it in serialize. related_name and related_query_name are used to make the serialization and point the nested relation.

And finally, your serialize could be like this (I exemplified it with a create method):

class MembershipSerializer(ModelSerializer):
    class Meta:
        fields = ("id", "join_date",)


class GroupSerializer(ModelSerializer):
    memberships = MembershipSerializer(many=True)

    class Meta:
        model = Group
        fields = ("id", "name", "memberships",)


class MemberSerializer(ModelSerializer):
    groups = GroupSerializer(many=True)

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

    def create(self):
        groups_data = validated_data.pop('groups')
        member = Member.objects.create(**validated_data)
        for group in groups_data:
            memberships_data = group.pop('memberships')
            Group.objects.create(member=member, **group)
            for memberhip in memberships:
                Membership.objects.create(group=group, **memberships)

The output will be:

{
  "id": 1,
  "name": "Paul McCartney",
  "groups": [
    {
      "id": 3,
      "name": "Beatles",
      "memberships": [
        {
          "id": 2,
          "join_date": "2018-08-08T13:43:45-0300"
        }
      ]
    }
  ]
}

In this output, I am not "nesting" the parent id but you can make it too, just declare into fields attributes.

Aipi
  • 2,076
  • 4
  • 18
  • 27
  • 1
    This removes the through model though and therefore doesn't fully answer the question. It works in this case, but there are use cases for through models that may need to be serialized. – partofthething May 06 '21 at 22:14
0

By looking at your output it seems you want to show membership inside groups and groups inside member. I would recommend editing the serializer to something like this.

class MemberSerializer(ModelSerializer):
    groups = GroupSerializer(many=True)         
    class Meta:
        model = Member
        fields = ("id","name","groups")

class GroupSerializer(ModelSerializer):
    membership = MembershipSerializer()        
    class Meta:
        model = Group
        fields = ("id","name","membership")

class MembershipSerializer(ModelSerializer):
    class Meta:
        model = Membership
        fields = "__all__"
Saket Khandelwal
  • 347
  • 1
  • 11