Take the 2-minute tour ×
Stack Overflow is a question and answer site for professional and enthusiast programmers. It's 100% free, no registration required.

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:

class Playlist(models.Model):
    provider = models.IntegerField()
    channel_id = models.CharField(max_length=100)
    channel_version = models.CharField(blank=True, max_length=100)
    start = models.DateTimeField()
    url = models.CharField(max_length=500)


class ChannelItem(models.Model):
    playlist = models.ForeignKey(Playlist, editable=False, related_name='channelitems')
    content_id = models.CharField(max_length=100)
    content_version = models.CharField(blank=True, max_length=100)

My Serializer:

class ChannelItemSerializer(serializers.ModelSerializer):
    class Meta:
        model = ChannelItem
        fields = ('content_id', 'content_version')
        exclude = ('id')
        depth = 1


class PlaylistSerializer(serializers.ModelSerializer):

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

channelitems = ChannelItemSerializer()

I use the curl to post the following data :

'{"provider":125,"channel_id":"xyz", "channel_version":"xsqt", 
"start":"2012-12-17T11:04:35","url":"http://192.168.1.83:8080/maaaaa",
"channelitems":[{"content_id":"0.flv", "content_version":"ss"},
{"content_id":"1.flv","content_version":"ss"}]}' http://localhost:8000/playlist_scheduler/playlists/

I receive the message:

HTTP/1.1 201 CREATED
Content-Type: application/json
Transfer-Encoding: chunked
Date: Mon, 17 Dec 2012 20:12:54 GMT
Server: 0.0.0.0

{"id": 25, "provider": 125, "channel_id": "xyz", "channel_version": "xsqt",
"start":"2012-12-17T11:04:35", "url": "http://localhost:8080/something",
"channelitems": []}
share|improve this question

3 Answers 3

up vote 8 down vote accepted

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.

share|improve this answer
    
what a pity ... it will be a very good feature when available... –  skopp Dec 18 '12 at 11:41
4  
it will come :) –  Tom Christie Dec 18 '12 at 12:01
5  
I've seen the term flat representation also used several times in the REST framework documentation, but can you give an example of what this means exactly? I fully understand the nested serializers, but I need to read-write, not just read. My options it seems are to flatten out the representation, or to write my own custom RelatedField? –  user798719 Jul 5 '13 at 4:41
2  
@TomChristie, It is ready now? –  avances123 Feb 11 at 23:15
    
@avances123 it seems so! see: github.com/tomchristie/django-rest-framework/blob/master/… –  fjsj Apr 13 at 1:36

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:

share|improve this answer

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
share|improve this answer

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.