django rest框架通过POST创建嵌套对象“模型”

时间:2012-12-17 20:28:14

标签: django django-rest-framework

我正在尝试POST一个新的嵌套对象,问题只是创建“顶级”对象(播放列表),但不要创建“ChannelItem”......

我的模特:

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

我使用curl发布以下数据:

'{"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/

我收到消息:

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": []}

4 个答案:

答案 0 :(得分:21)

嵌套表示do not currently support read-write,而应该是只读的。

您应该考虑使用平面表示,使用pk或超链接关系。

如果需要嵌套表示,您可能需要考虑使用两个单独的端点 - 平面可写端点和嵌套的只读端点。

答案 1 :(得分:6)

如果有人需要一个快速而肮脏的解决方案,我想出了一个我将在项目中临时使用的那个:

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

然后创建自己的NestedManyToManyField的子类:

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

MyOriginalSerializer的一个例子:

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

到目前为止,这对我来说很好。但要注意有一些干净的修复:

答案 2 :(得分:3)

经过长时间的努力,我制作了第一个版本的funcinasse ... 我相信,一些改进可以包含在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

答案 3 :(得分:1)

对我来说,我有一个混合的解决方法,我很好。即,创建一个具有以下内容的视图:

  • 非嵌套序列化程序表单中的ManyToMany字段
  • 将嵌套的ManyToMany字段别名为_objs作为后缀的变量,并将其指定为只读
  • 当您PUT返回服务器时,会核对两个别名字段并将结果存储在非嵌套序列化程序字段中

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

我不喜欢这个解决方案,但是在检索初始容器之后尝试单独查询和整理嵌套字段是很好的。