Django Rest Framework writable nested field using create()

23 阅读2分钟

在使用 Django Rest Framework 时,尝试编写一个创建方法来写入嵌套字段,但发现嵌套对象没有被写入。以下是示例代码:

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)
        Song.objects.create(**song_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:
            Messages.objects.get_or_create(owner=instance)

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

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',)
        #managed=True

解决方案

在 MessagesSerializer.create 方法中,需要先创建 Song 对象,然后将 Song 的外键传递给 Messages.create 方法。以下是修改后的代码:

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')
        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

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:
            Messages.objects.get_or_create(owner=instance)

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

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',)
        #managed=True