【问题标题】:django rest framework create nested objects "Models" by POSTdjango rest框架通过POST创建嵌套对象“模型”
【发布时间】:2012-12-04 23:47:23
【问题描述】:

我正在尝试发布一个新的嵌套对象,问题只是创建“顶部”对象(播放列表),但不要创建“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)

我的序列化器:

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

【问题讨论】:

    标签: django django-rest-framework


    【解决方案1】:

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

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

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

    【讨论】:

    • 我已经在 REST 框架文档中看到过多次使用平面表示这一术语,但你能举一个例子来说明这究竟是什么意思吗?我完全理解嵌套的序列化程序,但我需要读写,而不仅仅是阅读。我的选择似乎是展平表示,还是编写我自己的自定义相关字段?
    • @fjsj 您发布的链接返回 404。任何人都可以发布一个链接,显示它在哪里/如何执行此操作吗?
    • @JamesBerlin 似乎文件已移动。这是当前链接:github.com/tomchristie/django-rest-framework/blob/… 但这些测试已被注释掉。不知道为什么。
    • @TomChristie 现在支持嵌套表示吗?你能提供一个如何使用它的链接吗?
    【解决方案2】:

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

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

    到目前为止,这对我来说效果很好。但请注意,即将推出全新修复:

    【讨论】:

      【解决方案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
      

      【讨论】:

        【解决方案4】:

        对我来说,我有一个我可以接受的混合解决方法。即,创建一个视图:

        • ManyToMany 字段的非嵌套序列化程序形式
        • 将嵌套的 ManyToMany 字段别名为以_objs 为后缀的变量,并将其指定为只读
        • 当您PUT 返回服务器时,协调两个别名字段并将结果存储在未嵌套的序列化器字段中

        例如

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

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

        【讨论】:

          猜你喜欢
          • 2019-06-14
          • 1970-01-01
          • 2014-05-21
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2020-11-07
          • 2013-05-05
          • 1970-01-01
          相关资源
          最近更新 更多