Home > Enterprise >  Django Rest Framework: KeyError when Overriding 'update' Function in a Serializer
Django Rest Framework: KeyError when Overriding 'update' Function in a Serializer

Time:05-09

I have a User model and a Group model, which are presented below:

class User(models.Model):
    username = models.CharField(primary_key=True, max_length=32, unique=True)
    user_email = models.EmailField(max_length=32, unique=False)
    user_password = models.CharField(max_length=32)
    user_avatar_path = models.CharField(max_length=64)

class Group(models.Model):
    group_id = models.AutoField(primary_key=True)
    group_name = models.CharField(max_length=32, unique=False)
    group_admin = models.ForeignKey(User, on_delete=models.CASCADE, related_name='my_groups')
    members = models.ManyToManyField(User, related_name='groups', through='UserGroup')

Every group can have multiple users associated with it and every user can be associated to multiple groups, which is modeled with a ManyToManyField and a through model between the models. When I create a group, the user which creates the group is automatically assigned as the group admin and therefore added as a member of the group:

class MemberSerializer(serializers.ModelSerializer): # The nested serializer used within the GroupSerializer

    username = serializers.ReadOnlyField(source='user.username')

    class Meta:
        model = User
        fields = ['username']

class GroupSerializer(serializers.ModelSerializer):
    members = MemberSerializer(source='user_groups', many=True, required=False)
    group_admin = serializers.SlugRelatedField(slug_field='username', queryset=User.objects.all()) # A Group object is related to a User object by username

    class Meta:
        model = Group
        fields = ['group_id', 'group_name', 'group_admin', 'members']

    def create(self, validated_data): # Overriden so that when a group is created, the group admin is automatically declared as a member.
        group = Group.objects.create(**validated_data)
        group_admin_data = validated_data.pop('group_admin')
        group.members.add(group_admin_data)

        return group

    def update(self, instance, validated_data):
        members_data = validated_data.pop('members') # Comes from the request body
        group_id = self.kwargs['group_id'] # Comes from the URL
        add_remove = self.kwargs['add_remove'] # Comes from the URL 
        group = Group.objects.filter(group_id=group_id).first()
        if members_data is not None:
            if add_remove == 'add':
                for member in members_data:
                    group.members.add(member['username'])
            elif add_remove == 'remove':
                for member in members_data:
                    group.members.remove(member['username'])   
            
        return super().update(instance, validated_data)

Adding the user which created the group (the group admin) as a member is done successfully by overriding the create method of the GroupSerializer and then making a POST request as shown below:

{
    "group_name": "StackOverFlow",
    "group_admin": "bigboy"  
}

# which then results in the response when the group has been successfully created:

{
    "group_id": 1,
    "group_name": "StackOverFlow",
    "group_admin": "bigboy",
    "members": [
        {
            "username": "bigboy"
        }
    ]
}

I want to be able to update the members of the group through a PATCH request by adding or removing users associated with a group. I have attempted to do this by overriding the update method of GroupSerializer as shown above. The PATCH request is made as shown below with the URL of http://127.0.0.1:8000/group/1/add/update to add a user to a group:

{
    "members": [
        {
            "username": "small_man"
        }
    ]
}

The error that is received:

line 47, in update
    members_data = validated_data.pop('members') # Comes from the request body
KeyError: 'members'

I'm unsure if the method I'm attempting to add a user to a group, is a simple or even valid method but the KeyError is confusing because I assume that the validated data does include a members list.

EDIT

The error above has been solved.

I overrode the perform_update method of the GroupUpdate View as follows in order to see why my request data would not be validated:

    def perform_update(self, serializer):
        serializer=GroupSerializer(data=self.request.data, partial=True)
        serializer.is_valid(raise_exception=True)
        return super().perform_update(serializer)

The more detailed error that appeared was: TypeError: Direct assignment to the reverse side of a related set is prohibited. Use user_groups.set() instead. For reference, user_groups is the related_name of the UserGroup through model:

class UserGroup(models.Model): # Manually specified Junction table for User and Group
    user = models.ForeignKey(
        User, 
        on_delete=models.CASCADE, 
        related_name='user_groups'
        )
    group = models.ForeignKey(
        Group, 
        on_delete=models.CASCADE, 
        related_name='user_groups'
        )

CodePudding user response:

When you use source argument you can access you data using source value as a key, try this:

def update(self, instance, validated_data):
    members_data = validated_data.pop('user_groups')
  • Related