jwv jwv - 1 year ago 194
Python Question

Django Rest Framework writable nested field using create()

I'm trying to write a create method that will write my nested fields but am finding that the nested object isn't written.

This is the sample I was using:

class UserSerializer(serializers.ModelSerializer):
profile = ProfileSerializer()

class Meta:
model = User
fields = ('username', 'email', 'profile')

def create(self, validated_data):
profile_data = validated_data.pop('profile')
user = User.objects.create(**validated_data)
Profile.objects.create(user=user, **profile_data)
return user

But I'm failing to understand what the user=user refers to.

Here is my code:

class MessagesSerializer(serializers.HyperlinkedModelSerializer):
id = serializers.IntegerField(source='pk', read_only=True)
suggested_songs = SongSerializer()

class Meta:
model = Messages
fields = ('id','owner','url','suggested_songs',)
#fields = ('id','url','suggested_songs',)

def create(self, validated_data):
song_data = validated_data.pop('suggested_songs')
message = Messages.objects.create(**validated_data)
return message

class SongSerializer(serializers.HyperlinkedModelSerializer):
#id = serializers.IntegerField(source='pk', read_only=True)

class Meta:
model = Song
fields = ('id','title','artist','album','albumId','num_votes','cleared')
read_only_fields = ('song_id')

class Messages(models.Model):
owner = models.OneToOneField(User, primary_key=True, related_name='user_messages', editable=False) #TODO, change owner to 'To'
#suggested_songs = models.ManyToManyField(Song, related_name='suggested_songs')
suggested_songs = models.ForeignKey(Song, null=True, blank=True)

# If a user is added, this runs.
@receiver(post_save, sender=User)
def create_friend_for_user(sender, instance=None, created=False, **kwargs):
if created:

# Same as above, but for deletion
@receiver(pre_delete, sender=User)
def delete_friend_for_user(sender, instance=None, **kwargs):
if instance:

class Song(models.Model):
A model which holds information about the songs.
#song_id = models.IntegerField(primary_key=True)
title = models.CharField(max_length=150, blank=True, default='')
artist = models.CharField(max_length=150, blank=True, default='')
album = models.CharField(max_length=150, blank=True, default='')
albumId = models.CharField(max_length=150, blank=True, default='')
num_votes = models.IntegerField(default=0, blank=True)
cleared = models.BooleanField(default=False, blank=True)

class Meta:
ordering = ('title',)

Answer Source

I think that the issue might be in the MessageSerializer.create method:

def create(self, validated_data):
        # here you are popping the suggested songs
        song_data = validated_data.pop('suggested_songs')
        # so when you create the message here the foreign key is set to NULL
        message = Messages.objects.create(**validated_data)
        # and here you create the Song instance correctly but it is not
        # associated with the message
    return message

You need to pass the foreign key to the Messages.create method like in the example you have.

def create(self, validated_data):
        song_data = validated_data.pop('suggested_songs')
        song = Song.objects.create(**song_data)
        # song need to be created first because the foreign key is in
        # the Messages model
        message = Messages.objects.create(suggested_songs=song, **validated_data)
        return message

I hope this helps!

Recommended from our users: Dynamic Network Monitoring from WhatsUp Gold from IPSwitch. Free Download