【问题标题】:Django REST Framework nested resource key "id" unaccessibleDjango REST Framework 嵌套资源键“id”无法访问
【发布时间】:2015-03-13 14:05:25
【问题描述】:

所以我有以下结构:

ClientFile 属于所有者(类名 = 联系人)。 我正在尝试使用 API 创建一个 Clientfile。该请求包含以下数据:

{
  name: "Hello!"
  owner: {
    id: 1,
    first_name: "Charlie",
    last_name: "Watson"
  }
}

我根据我的结构创建了序列化程序。希望这个 API 调用会创建一个名为“Hello!”的客户端文件。和联系人 id 1 作为所有者:

class ContactSerializer(serializers.ModelSerializer):
  class Meta:
    model = Contact
    fields = (
      'id',
      'first_name',
      'last_name',
    )

class ClientfileSerializer(serializers.ModelSerializer):

  owner = ContactSerializer(read_only=False)

  class Meta():
    model = Clientfile
    fields = (
      'id',
      'name',
      'owner',
    )

  def create(self, validated_data):

    owner = Contact.objects.get(pk=validated_data['owner']['id'])

我确实进入了 create 方法。但是,我需要的唯一字段 (['owner']['id']) 无法访问。如果我这样做 print ['owner']['first_name'] 它确实会返回“查理”。但是由于某些原因,该 ID 似乎无法访问...

发生这种情况的任何原因?我错过了什么吗? (我是 Django 新手)


解决方案:刚刚发现 ID 没有显示在首位的原因是因为我必须在这样的字段中声明它:希望这会有所帮助。

class ContactSerializer(serializers.ModelSerializer):

  id = serializers.IntegerField() # ← Here

  class Meta:
    model = Contact
    fields = (
      'id',
      'first_name',
      'last_name',
    )

【问题讨论】:

    标签: python django django-rest-framework


    【解决方案1】:

    在 Django REST Framework 中,AutoField 字段(那些自动生成的)默认为只读。来自the docs

    read_only

    将此设置为True 以确保在以下情况下使用该字段 序列化表示,但在创建或 在反序列化期间更新实例。

    默认为False

    你可以通过inspecting your serializer在你的shell中打印这个表示来看到这个:

    serializer = ClientfileSerializer()
    print repr(serializer)
    

    您可以通过针对extra_kwargs 中的 id 字段设置 read_only=False 来覆盖此设置:

    class ContactSerializer(serializers.ModelSerializer):
      class Meta:
        model = Contact
        fields = (
          'id',
          'first_name',
          'last_name',
        )
        extra_kwargs = {'id': {'read_only': False}}
    
    class ClientfileSerializer(serializers.ModelSerializer):
    
      owner = ContactSerializer(read_only=False)
    
      class Meta():
        model = Clientfile
        fields = (
          'id',
          'name',
          'owner',
        )
        extra_kwargs = {'id': {'read_only': False}}
    

    【讨论】:

    • 我试过了。它解决了问题,但又产生了另一个问题:现在我无法创建联系人。它会抛出一个错误,说明 ID 字段是必需的...
    • 您是否也尝试过对 clientfileserializer 的 id 字段应用只读?
    • 我遇到了和@MonsieurNinja 一样的问题,这使得需要手动设置 id。
    【解决方案2】:

    好的,所以我找到了另一种可行的方法。 我为所有者关系添加了一个 IntegerField 序列化程序。我还必须将所有者关系设置为 read_only=True。

    这是我通过 POST 发送的 json:

    {
      name: "Hello!"
      owner_id: 1
    }
    

    这是我的序列化器:

    class ClientfileSerializer(serializers.ModelSerializer):
    
      owner_id = serializers.IntegerField()
      owner = ContactSerializer(read_only=True)
    
      class Meta():
        model = Clientfile
        fields = (
          'id',
          'owner_id',
          'owner',
        )
    

    它看起来不像第一种方式那么酷,但它确实有效。 另外我不想创建一个新的所有者,而只是选择一个已经在数据库中的所有者。所以也许只有 ID 而不是通过 Json 发布的完整信息集更具语义。

    【讨论】:

      【解决方案3】:

      你可以试试这样的:

      class YourModelSerializer(serializers.ModelSerializer):
      
          class Meta:
              model = YourModel
              fields = ('id', 'field1', 'field2')
      
          def to_internal_value(self, data):
              """
              Dict of native values <- Dict of primitive datatypes.
              Add instance key to values if `id` present in primitive dict
              :param data:
              """
              obj = super(YourModelSerializer, self).to_internal_value(data)
              instance_id = data.get('id', None)
              if instance_id:
                  obj['instance'] = YourModel.objects.get(id=instance_id)
              return obj
      

      如果 request.data 有“id”键,那么在序列化程序验证的数据中你应该有“instance”键。

      您也可以只添加“id”而不是完整的实例/对象。

      【讨论】:

      • 这似乎解决了validated_data中缺少id的问题,没有任何副作用,比如将id字段设置为非只读。 (导致它是必需的,即使在创建还没有 id 的新对象时)
      【解决方案4】:

      票数最高的答案确实解决了这个问题,但它引发了一个新问题,正如 cmets 中提到的那样,我们无法再创建新记录,因为它会引发 ac 异常说是必需的。我们可以将 id 设置为 required=False 然后 id 将在 valid_data 中可用,并且不需要手动设置它

      id = serializers.IntegerField(required=False) <- Like this
      
      class Meta:
          model = Details
          fields = ('id', 'product_name', 'description', 'specification', 'make_model',
                    'brand', 'quantity',)
      

      【讨论】:

        猜你喜欢
        • 2014-12-15
        • 2017-10-19
        • 2019-12-23
        • 2016-01-22
        • 2015-01-10
        • 1970-01-01
        • 2015-11-11
        • 2017-09-19
        • 1970-01-01
        相关资源
        最近更新 更多