souldeux
souldeux

Reputation: 3755

Django Rest Framework - Override serializer create method for model with self-referential M2M field

I have a model that looks like this:

class Profile(models.Model):
    following = models.ManyToManyField('Profile', related_name='followed')
    admin_notes = models.TextField()

And a Django Rest Framework serializer that looks like this:

class ProfileSerializer(serializers.HyperlinkedModelSerializer):
    class Meta:
        model = Profile
        fields = '__all__'

When creating a new Profile I want to interrupt the save/create to set admin_notes programmatically. I know how do to this inside a function-based view, but I am using DRF's viewsets this time and so I want to do this inside the serializer. The self-referential relationship is giving me trouble, though.

I tried this within the ProfileSerializer:

def create(self, **validated_data):
    p = Profile(**validated_data)
    p.admin_notes = 'Test'
    p.save()

This, as well as trying Profile.objects.create instead of just making a new Profile instance in the first line, resulted in the following error:

Exception Value: "<Profile>" needs to have a value for field "id" before this many-to-many relationship can be used.

Trying this:

def create(self, validated_data):
    validated_data['admin_notes'] = 'Test'
    return super(ProfileSerializer, self).create(**validated_data)

Results in this error:

Exception Value: create() got an unexpected keyword argument 'following'

I understand that Django creates m2m relationships in a two-step-save process, so the error makes sense but still leaves me stumped as to how I can move forward. How can I set the value of admin_notes within the serializer given the way my model is set up?

Upvotes: 1

Views: 5685

Answers (1)

davecaputo
davecaputo

Reputation: 341

Try this (assuming that you are passing the ids of the Profile instances to your m2m field):

def create(self, validated_data):

    # First, remove following from the validated_data dict...
    following_data = validated_data.pop('following', None)

    # Set the admin_notes custom value...
    validated_data['admin_notes'] = 'Test'

    # Create the object instance...
    profile = Profile.objects.create(**validated_data)

    # Finally, add your many-to-many relationships...
    if following_data:
        for data in following_data:
            profile.followed.add(Profile.objects.get(**data))
    return profile

Upvotes: 3

Related Questions