[Django]-Django rest framework create nested objects "Models" by POST

22👍

Nested representations do not currently support read-write, and should instead be read-only.

You should probably look into using a flat representation instead, using pk or hyperlinked relations.

If you need the nested representation, you may want to consider having two separate endpoints – a flat writable endpoint, and a nested read-only endpoint.

6👍

If someone needs a quick-and-dirty solution for that, I came up with this one I’ll be temporary using in a project:

class NestedManyToManyField(serializers.WritableField):
    def to_native(self, value):
        serializer = self.Meta.serializer(value.all(), many=True, context=self.context)
        return serializer.data
    def from_native(self, data):
        serializer = self.Meta.serializer(data=data, many=True, context=self.context)
        serializer.is_valid()
        serializer.save()
        return serializer.object
    class Meta:
        serializer = None

Then create your own subclass of NestedManyToManyField:

class TopicNestedSerializer(NestedManyToManyField):
    class Meta:
        serializer = MyOriginalSerializer

An example of MyOriginalSerializer:

class MyOriginalSerializer(serializers.ModelSerializer):
    class Meta:
        model = models.MyModel
        fields = ('id', 'title',)

This works fine for me so far. But be aware there are clean fixes coming:

3👍

after a long effort I made a first version that funcinasse …
I believe that with some improvement could be included within the ModelSerializer

class ChannelItemSerializer(serializers.ModelSerializer):

    class Meta:
        model = ChannelItem
        fields = ('id', 'content_id', 'content_version')

    def field_from_native(self, data, files, field_name, into):
        try:
            if self._use_files:
                _files = files[field_name]
            else:
                _data = data[field_name]
        except KeyError:
            if getattr(self, 'default', None):
                _data = self.default
            else:
                if getattr(self, 'required', None):
                    raise ValidationError(self.error_messages['required'])
                return

        if type(_data) is list:
            into[field_name] = [] 
            for item in _data:
                into[field_name].append(self._custom_from_native(item))
        else:
            into[field_name] = self._custom_from_native(_data)


    def _custom_from_native(self, data):
        self._errors = {}
        if data is not None:
            attrs = self.restore_fields(data, None)
            attrs = self.perform_validation(attrs)
        else:
            self._errors['non_field_errors'] = ['No input provided']

        if not self._errors:
            return self.restore_object(attrs, instance=getattr(self, 'object', None))




class PlaylistSerializer(serializers.ModelSerializer):

    class Meta:
        model = Playlist
        fields = ('id', 'provider', 'channel_id', 'channel_version', 'start', 'url', 'channel_items')
        depth = 1

    channel_items = ChannelItemSerializer()

    def restore_object(self, attrs, instance=None):
        self.foreign_data = {}

        for (obj, model) in self.opts.model._meta.get_all_related_objects_with_model():
            field_name = obj.field.related_query_name()
            if field_name in attrs:
                self.foreign_data[field_name] = attrs.pop(field_name)


        return super(PlaylistSerializer, self).restore_object(attrs, instance)

    def save(self, save_m2m=True):
        super(PlaylistSerializer, self).save(save_m2m)

        if getattr(self, 'foreign_data', None):
            for accessor_name, object_list in self.foreign_data.items():
                setattr(self.object, accessor_name, object_list)
            self.foreign_data = {}

        return self.object
👤skopp

1👍

For me, I have a hybrid workaround that I’m OK with. Namely, create a view that has:

  • the ManyToMany field in its un-nested serializer form
  • alias the nested ManyToMany field into a variable with _objs as the suffix and specify it as as read only
  • when you PUT back to the server reconcile the two aliased fields and store the result in the un-nested serializer field

e.g.

class MSerializer(serializers.HyperlinkedModelSerializer):
    foo_objs = TempSensorSerializer(source='foos', many=True, allow_add_remove=True,required=False,read_only=True)
    class Meta:
        model = M
        fields = ('url', 'foos', 'foo_objs')

I don’t love this solution, but it beats trying to separately query and collate the nested fields after retrieving the initial container.

Leave a comment