I\'m trying POST a new a Nested Object, the problem is just create the \"top\" object (Playlist), but don\'t create the \"ChannelItem\"...
My Models:
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
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.
For me, I have a hybrid workaround that I'm OK with. Namely, create a view that has:
_objs
as the suffix and specify it as as read onlyPUT
back to the server reconcile the two aliased fields and store the result in the un-nested serializer fielde.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.
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: